Skip to content
Developers

Connect InnoReach to whatever you already use

A REST API for reading and updating leads, plus webhooks so your CRM hears about changes the moment they happen — no polling.

Base URL and authentication

Every endpoint lives under https://innoreach.innobrains.pk/api/v1/ and is authenticated with an API key you create in Developer settings.

curl https://innoreach.innobrains.pk/api/v1/leads \
  -H "Authorization: Bearer inr_your_key_here"

An X-API-Key header works too, if that suits your HTTP client better. Keys are stored as a hash, so the value is shown once at creation and cannot be recovered afterwards — if you lose it, revoke the key and make a new one.

Scopes

A key carries either read, write, or both. Give a key only what it needs: a reporting integration that can also rewrite your pipeline is a liability, not a convenience. A request outside a key's scope returns 403 with insufficient_scope.

Rate limits

120 read requests and 60 write requests per minute. Exceeding either returns 429; wait for the window to reset rather than retrying immediately.

Reference

Endpoints

MethodPathScopeWhat it does
GET /api/v1/leads read List leads, filtered and paginated
GET /api/v1/leads/{id} read One lead, with its activity history
PATCH /api/v1/leads/{id} write Update status, notes or follow-up date
POST /api/v1/leads/{id}/activities write Record a call, message or meeting
GET /api/v1/account read Licence, credit balance and lead counts
GET /api/v1/meta read Valid statuses, activity types and webhook events

Keeping a CRM in sync

The field that matters for syncing is updated_since. Store the highest updated_at you have seen, and pass it back on the next call to get only what has changed.

GET /api/v1/leads?updated_since=2026-08-26T09:00:00Z&per_page=200

The boundary second is inclusive. Timestamps are stored to the second, so an exclusive comparison would silently drop anything written during the same second as your last poll — which is exactly the kind of bug that loses a customer's lead and is never noticed.

Filters

  • status — new, contacted, interested, customer, not_interested
  • city and category — exact match
  • has_email / has_phone — only leads that have one
  • per_page — up to 200, default 50

Response shape

{
  "data": [
    {
      "id": 1042,
      "name": "Acme Traders",
      "phone": "03027138708",
      "phone_international": "+923027138708",
      "whatsapp_url": "https://wa.me/923027138708",
      "email": "[email protected]",
      "website": "https://acme.pk",
      "address": "Jail Road, Lahore",
      "category": "Wholesaler",
      "city": "Lahore",
      "country": "Pakistan",
      "rating": 4.3,
      "status": "interested",
      "notes": "Wants a quote by Friday",
      "follow_up_at": "2026-08-29T10:00:00+00:00",
      "updated_at": "2026-08-26T09:14:02+00:00"
    }
  ],
  "meta": { "page": 1, "per_page": 50, "total": 137, "has_more": true }
}

phone_international is the field to dial or message with — it is normalised from the local format, so you do not need to know each country's dialling rules.

Writing back

curl -X PATCH https://innoreach.innobrains.pk/api/v1/leads/1042 \
  -H "Authorization: Bearer inr_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{"status":"customer","notes":"Signed a 6-month contract"}'
curl -X POST https://innoreach.innobrains.pk/api/v1/leads/1042/activities \
  -H "Authorization: Bearer inr_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{"type":"call","note":"Discussed pricing","outcome":"interested"}'

Webhooks

Rather than polling, register an HTTPS endpoint and we will POST to it when something changes. Events fire wherever the change came from — the Chrome extension, the Android app, the dashboard, or this API.

  • lead.created — a new business was collected
  • lead.updated — status, notes, follow-up or contact details changed
  • activity.logged — a call or message was recorded

Verifying a delivery

Every request carries X-InnoReach-Signature, an HMAC-SHA256 of the exact request body using your endpoint's signing secret. Verify it before trusting the payload — otherwise anyone who learns your URL can post fake leads into your CRM.

// Node.js
const crypto = require('crypto');

function verify(rawBody, headerValue, secret) {
  const expected = 'sha256=' + crypto
    .createHmac('sha256', secret)
    .update(rawBody)
    .digest('hex');

  // Constant-time compare, so a timing attack cannot guess the signature.
  return crypto.timingSafeEqual(
    Buffer.from(expected),
    Buffer.from(headerValue),
  );
}
// PHP
$expected = 'sha256=' . hash_hmac('sha256', $rawBody, $secret);

if (! hash_equals($expected, $_SERVER['HTTP_X_INNOREACH_SIGNATURE'])) {
    http_response_code(401);
    exit;
}

Verify against the raw body, not a re-encoded version of the parsed JSON — re-encoding changes key order and whitespace, and the signature will never match.

Delivery and retries

Return any 2xx status to acknowledge. We retry three times with a short backoff, and an endpoint that fails ten times in a row is paused automatically so a dead URL does not queue forever. You can re-enable it from Developer settings once it is fixed, which also clears the failure count.

Payload

{
  "event": "lead.created",
  "sent_at": "2026-08-26T09:14:02+00:00",
  "data": {
    "id": 1042,
    "name": "Acme Traders",
    "phone_international": "+923027138708",
    "email": "[email protected]",
    "city": "Lahore",
    "status": "new"
  }
}
When things go wrong

Error responses

StatusErrorWhat to do
401 missing_api_key Send the key in an Authorization or X-API-Key header
401 invalid_api_key The key is unknown, revoked or expired — create a new one
403 insufficient_scope The key lacks the scope this endpoint needs
403 account_suspended Contact support
404 The lead does not exist, or belongs to another account
422 validation A field is missing or invalid; the response lists which
429 Rate limited — wait for the window to reset

A lead belonging to another account returns 404, not 403 — confirming that an id exists would leak information about other customers.

Developers

Build the integration you need

Create a key, point it at your CRM, and pull your leads in. If something is missing from the API, tell us — it is a young surface and we would rather add what people actually use.

  • REST API
  • Signed webhooks
  • Scoped keys