Arraxis API

Version 1. Create and read leads, move a lead along your pipeline, add notes, and get told when something changes.

Start here

In Arraxis open Settings, then Lead sources, then Keys for other apps, and press Make a key. Copy it straight away: for your safety Arraxis shows it only once. Every call goes to this address:

https://app.arraxis.site/api/v1

Check that your key works:

curl -H "Authorization: Bearer arx_live_YOUR_KEY" https://app.arraxis.site/api/v1/whoami

Everything is JSON. Field names are snake_case, times are ISO 8601 in UTC (2026-09-30T09:12:44.120Z), and an empty value is null, never a dash. Fields you send that Arraxis does not know are ignored; fields Arraxis adds to its answers later must be ignored by your code too.

Authentication

Send the key in the header Authorization: Bearer <key>. Never put it in the address: a key in the address is refused (400) because addresses end up in logs. A key can only do what was ticked when it was made:

A key acts on the whole workspace, money values included. Keep it secret like a password. An owner or manager can switch a key off at any time; if the person who made a key leaves the team, the key is paused until an owner turns it back on.

Errors

Every error is application/problem+json (RFC 9457): type, title, status, detail (what to fix, in plain words), code, request_id and, for 400 and 422, errors[] with the field name of each problem. Quote the request_id when you ask for help.

HTTPcodeWhen
400invalid_requestThe request is not valid. The body is not JSON, is not an object, a query value is wrong, a cursor is wrong, or the key was sent in the address.
401unauthorizedMissing or invalid API key. The Authorization header is missing, the key is not valid, was switched off, has expired or is paused.
403forbiddenThis key cannot do that. The key lacks the permission for this call, or the workspace is not active.
404not_foundNot found. The id does not exist, or belongs to another workspace (never 403: ids cannot be probed).
409idempotency_conflictIdempotency-Key reused with a different request. The same Idempotency-Key was sent with a different method, path or body.
409idempotency_in_progressThe first request with this Idempotency-Key is still running. Retry after the Retry-After seconds.
409conflictConflict. The lead changed at the same moment, a booked time was just taken, the source is paused, or a limit was reached.
409stage_not_allowedThis stage move is not allowed. The pipeline forbids the move. Send override: true to force it.
413payload_too_largeRequest body too large. The body is over 64 KB.
415unsupported_media_typeSend JSON. A write was sent without Content-Type: application/json.
422validation_failedSome fields are not valid. One or more fields are wrong; errors[] lists each with the field name.
429rate_limitedToo many requests. The per-key limit (120 requests a minute by default) or the bad-key limit was reached. Wait Retry-After seconds.
500internal_errorSomething went wrong. An unexpected error. Quote request_id when you contact support.

Idempotency

Every POST accepts an Idempotency-Key header (1 to 200 visible characters). Send the same key with the same body and you get the first answer back, with the header Idempotency-Replayed: true, for 24 hours. The same key with a different body is a 409. Use something unique per event, such as your entry id. Creating a lead also has its own protection: send external_id (unique per source) and a repeat returns the same lead.

Pagination

Lists are newest first. limit is 1 to 100 (25 by default). The answer is { "data": [...], "has_more": true, "next_cursor": "..." }; pass next_cursor as cursor to get the next page, with the same filters. Pages never skip or repeat a lead that did not change. To poll for changes use updated_since.

Rate limits

A key may make 120 requests a minute. Every answer carries X-RateLimit-Limit and X-RateLimit-Remaining; over the limit you get 429 with Retry-After (seconds). The API sends no CORS headers: call it from a server, never from a browser page. Changes inside version 1 only ever add things.

Endpoints

GET/api/v1/whoami

Who this key is · key needs any

Any valid key. Use it as the connection test.

Possible errors: 401, 403, 429.

GET/api/v1/leads

List leads, newest first · key needs leads:read

sortqueryreceived_at (default) or updated_at; always newest first.
stagequeryComma list of stage keys (at most 20).
sourcequeryComma list of source keys (at most 20); meta also matches each Meta form.
owner_emailqueryOne member's email; none = unassigned.
qqueryName, email or phone contains (at most 100 characters).
phonequeryExact phone, normalised with the workspace's region.
emailqueryExact email, any case.
received_sincequeryISO instant, inclusive.
received_beforequeryISO instant, exclusive.
updated_sincequeryISO instant: the polling filter for Zapier and Make.

Possible errors: 400, 401, 403, 429.

POST/api/v1/leads

Create a lead · key needs leads:write

Goes through the same intake core as every other source: duplicates, attribution, assignment and notifications work the same. A lead with the same phone as an open lead becomes a re-enquiry (outcome merged).

Example body:

{
  "name": "Alex Morgan",
  "phone": "+1 555 0100",
  "email": "alex.morgan@example.com",
  "service": "Consultation",
  "location": "Downtown",
  "source": "api",
  "external_id": "typeform-8812",
  "attribution": {
    "utm_source": "facebook"
  }
}

Answer 201:

{
  "outcome": "created",
  "lead": {
    "id": "6f1c2d0e-8a41-4b7e-9c55-3e2f1a9b7c10",
    "url": "https://app.arraxis.site/leads/6f1c2d0e-8a41-4b7e-9c55-3e2f1a9b7c10",
    "created_at": "2026-09-30T09:12:44.120Z",
    "updated_at": "2026-09-30T09:12:44.120Z",
    "received_at": "2026-09-30T09:12:44.120Z",
    "name": "Alex Morgan",
    "phone": "+15550100",
    "phone_raw": "+1 555 0100",
    "email": "alex.morgan@example.com",
    "city": null,
    "gender": null,
    "age": null,
    "stage": {
      "key": "new",
      "name": "New",
      "kind": "new"
    },
    "source": {
      "id": "a41f0b7c-0000-4000-8000-000000000001",
      "key": "api",
      "name": "API",
      "kind": "api"
    },
    "owner": null,
    "service_interest": "Consultation",
    "service": null,
    "location": "Downtown",
    "tags": [],
    "custom": {
      "preferred_time": "Morning"
    },
    "value": null,
    "booked_at": null,
    "landed_at": null,
    "won_at": null,
    "lost_at": null,
    "next_action_at": "2026-09-30T09:12:44.120Z",
    "external_id": "typeform-8812",
    "attribution": {
      "utm_source": "facebook",
      "utm_campaign": "autumn-offer"
    },
    "answers": {},
    "ads": {
      "platform": null,
      "campaign_id": null,
      "campaign_name": null,
      "adset_id": null,
      "ad_id": null,
      "form_id": null
    },
    "duplicate_of": null
  }
}

Possible errors: 400, 401, 403, 409, 413, 415, 422, 429.

GET/api/v1/leads/{id}

One lead · key needs leads:read

A merged lead answers with its survivor and the header Arraxis-Merged-Into.

idpath, requiredThe lead's id.

Answer 200:

{
  "id": "6f1c2d0e-8a41-4b7e-9c55-3e2f1a9b7c10",
  "url": "https://app.arraxis.site/leads/6f1c2d0e-8a41-4b7e-9c55-3e2f1a9b7c10",
  "created_at": "2026-09-30T09:12:44.120Z",
  "updated_at": "2026-09-30T09:12:44.120Z",
  "received_at": "2026-09-30T09:12:44.120Z",
  "name": "Alex Morgan",
  "phone": "+15550100",
  "phone_raw": "+1 555 0100",
  "email": "alex.morgan@example.com",
  "city": null,
  "gender": null,
  "age": null,
  "stage": {
    "key": "new",
    "name": "New",
    "kind": "new"
  },
  "source": {
    "id": "a41f0b7c-0000-4000-8000-000000000001",
    "key": "api",
    "name": "API",
    "kind": "api"
  },
  "owner": null,
  "service_interest": "Consultation",
  "service": null,
  "location": "Downtown",
  "tags": [],
  "custom": {
    "preferred_time": "Morning"
  },
  "value": null,
  "booked_at": null,
  "landed_at": null,
  "won_at": null,
  "lost_at": null,
  "next_action_at": "2026-09-30T09:12:44.120Z",
  "external_id": "typeform-8812",
  "attribution": {
    "utm_source": "facebook",
    "utm_campaign": "autumn-offer"
  },
  "answers": {},
  "ads": {
    "platform": null,
    "campaign_id": null,
    "campaign_name": null,
    "adset_id": null,
    "ad_id": null,
    "form_id": null
  },
  "duplicate_of": null
}

Possible errors: 401, 403, 404, 429.

POST/api/v1/leads/{id}/stage

Move a lead to a stage · key needs leads:write

The stage's requires (see GET /stages) decide which of date, time, amount, reason and service are needed. Sending the current stage again is a no-op. A key without leads:read gets ApiLeadRef back.

idpath, requiredThe lead's id.

Example body:

{
  "stage": "booked",
  "date": "2026-10-02",
  "time": "11:00",
  "service": "Consultation"
}

Possible errors: 400, 401, 403, 404, 409, 413, 415, 422, 429.

POST/api/v1/leads/{id}/notes

Add a note to a lead · key needs leads:write

idpath, requiredThe lead's id.

Example body:

{
  "text": "Called back, will decide on Friday."
}

Possible errors: 400, 401, 403, 404, 413, 415, 422, 429.

GET/api/v1/stages

The stages of the default pipeline, in order · key needs leads:read

Possible errors: 401, 403, 429.

GET/api/v1/sources

The lead sources of the workspace · key needs leads:read

Use a key as the source of POST /leads.

Possible errors: 401, 403, 429.

GET/api/v1/fields

The custom lead fields · key needs leads:read

Keys of the custom object of POST /leads.

Possible errors: 401, 403, 429.

GET/api/v1/hooks

The addresses this key registered for events · key needs hooks:manage

Possible errors: 401, 403, 429.

POST/api/v1/hooks

Register an address for events (REST hook subscribe) · key needs hooks:manage

The signing secret is returned once.

Example body:

{
  "url": "https://hooks.zapier.com/hooks/standard/123/abc/",
  "events": [
    "lead.created"
  ]
}

Possible errors: 400, 401, 403, 409, 413, 415, 422, 429.

DELETE/api/v1/hooks/{id}

Unregister an address (REST hook unsubscribe) · key needs hooks:manage

Only hooks this key created; asking again answers 404.

idpath, requiredThe lead's id.

Possible errors: 401, 403, 404, 429.

GET/api/v1/hooks/sample

Example events for a type · key needs hooks:manage or leads:read

An array (not a list object) of up to 3 events of exactly the delivered shape, for an app's editor.

eventquery, requiredAn event type, e.g. lead.created.

Possible errors: 400, 401, 403, 429.

GET/api/v1/openapi.json

This document · key needs none

No key needed.

Possible errors: none.

curl -X POST https://app.arraxis.site/api/v1/leads \
  -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" -H "Idempotency-Key: typeform-8812" \
  -d '{"name":"Alex Morgan","phone":"+1 555 0100","service":"Consultation","location":"Downtown","source":"api","external_id":"typeform-8812"}'

Webhooks

Arraxis can send each new lead, and other changes, to an address you register: paste it in Settings, Lead sources, Send leads to other apps (press Send a test to see one arrive), or register it through the API (POST /hooks). Each message is an HTTPS POST of JSON. Answer with any 2xx. Anything else is retried 8 times over about 31 hours (1 minute, 5 minutes, 30 minutes, 2, 5, 10 and 14 hours); a 410 answer switches the address off at once, and an address that has failed for 3 days is switched off and your owners are told. Redirects are never followed. Messages older than 7 days cannot be sent again.

lead.createdNew lead
lead.updatedDetails changed
lead.stage_changedStage changed
lead.assignedAssigned
lead.note_addedNote added
lead.reinquiryEnquired again
lead.mergedLeads merged
appointment.bookedAppointment booked
appointment.rescheduledAppointment moved
appointment.cancelledAppointment cancelled
appointment.completedAppointment completed
appointment.no_showDid not show up
appointment.confirmedAppointment confirmed
pingTest

The body is { id, type, timestamp, workspace_id, actor, data }. id is the same for every address and every retry: dedupe on it (or on webhook-id). data.lead is the full lead as it is when the message is built; data.change holds the event's own facts (for a stage change, from and to stage keys). actor says who caused it: a Zap that writes back to Arraxis can ignore events whose actor.kind is api_key to avoid a loop.

{
  "id": "evt_1c7e2d4a-3f1b-4a51-9a0c-2b9d8e7f6a51",
  "type": "lead.stage_changed",
  "timestamp": "2026-09-30T10:04:11.502Z",
  "workspace_id": "0b1e5c3a-…",
  "actor": {
    "kind": "member"
  },
  "data": {
    "lead": {
      "id": "6f1c2d0e-…",
      "name": "Alex Morgan",
      "phone": "+15550100",
      "stage": {
        "key": "booked",
        "name": "Booked",
        "kind": "booked"
      },
      "…": "the full lead"
    },
    "appointment": null,
    "note": null,
    "change": {
      "from": "new",
      "to": "booked",
      "amount": null,
      "reason": null
    }
  }
}

Messages follow the Standard Webhooks signing scheme. Each carries three headers: webhook-id (msg_…, the same on every retry), webhook-timestamp (unix seconds of this attempt) and webhook-signature (v1,<base64>; two values separated by a space while you are replacing a secret). The signature is an HMAC-SHA256 of id.timestamp.body with your signing secret (whsec_…, base64-decoded). Verify it against the raw body, refuse timestamps more than 5 minutes old, and answer 2xx only after that:

// Node (no library)
import { createHmac, timingSafeEqual } from "node:crypto";
export function verifyArraxis(secret, headers, rawBody) {
  const id = headers["webhook-id"], ts = headers["webhook-timestamp"], sigs = String(headers["webhook-signature"] || "");
  if (!id || !ts || Math.abs(Date.now() / 1000 - Number(ts)) > 300) return false;
  const want = Buffer.from("v1," + createHmac("sha256", Buffer.from(secret.replace(/^whsec_/, ""), "base64")).update(`${id}.${ts}.${rawBody}`).digest("base64"));
  return sigs.split(" ").some((s) => { const got = Buffer.from(s); return got.length === want.length && timingSafeEqual(got, want); });
}
// PHP
function arraxis_verify(string $secret, array $h, string $raw): bool {
  $id = $h['webhook-id'] ?? ''; $ts = $h['webhook-timestamp'] ?? ''; $sigs = $h['webhook-signature'] ?? '';
  if ($id === '' || abs(time() - (int)$ts) > 300) return false;
  $key = base64_decode(preg_replace('/^whsec_/', '', $secret));
  $want = 'v1,' . base64_encode(hash_hmac('sha256', "$id.$ts.$raw", $key, true));
  foreach (explode(' ', $sigs) as $s) if (hash_equals($want, $s)) return true;
  return false;
}

Zapier's "Catch Hook" and Make's "Custom webhook" do not check signatures, so for them the unguessable address is the credential: keep it private.

Zapier

Leads into Arraxis: make a key that can add leads. In your Zap use Webhooks by Zapier, POST to https://app.arraxis.site/api/v1/leads with payload type JSON; data name, phone, email, service and external_id (the trigger's entry id); headers Authorization: Bearer arx_live_… and Idempotency-Key (the entry id). Give each app its own source, or prefix the id with the app name, because external_id is unique per source. The test step sends a real lead: delete it afterwards or test with your own number.

Leads out of Arraxis: in Zapier start with Webhooks by Zapier, Catch Hook and copy its address. In Arraxis paste it under Send leads to other apps, tick New lead, save and press Send a test; then press Test trigger in Zapier to see the sample lead's fields.

A private Zapier integration can subscribe for you: POST /hooks with { "url": "{{bundle.targetUrl}}", "events": ["lead.created"] } to subscribe, DELETE /hooks/{id} to unsubscribe, and GET /hooks/sample?event=lead.created for the editor's sample data. The key needs all three permissions.

Make

Into Arraxis: use HTTP, Make a request: URL https://app.arraxis.site/api/v1/leads, method POST, headers Authorization and Idempotency-Key, body type Raw, content type JSON, and Parse response: Yes. A 422 answer names the field that is wrong in errors[].field.

Out of Arraxis: use Webhooks, Custom webhook, copy its address, paste it under Send leads to other apps, press Redetermine data structure in Make and Send a test in Arraxis. Make turns off a webhook that is not attached to a scenario for 5 days and then answers 410; Arraxis then switches the address off.

WordPress plugin

Arraxis Leads sends your website's form submissions (Contact Form 7, WPForms, Gravity Forms, Elementor, Fluent Forms, Forminator) to Arraxis, with the page they were sent from and the ad click ids of the visit. Download it from Settings, Lead sources, add a WordPress website source, upload the zip in wp-admin under Plugins, Add New, Upload Plugin, then paste the key shown in Arraxis under Settings, Arraxis Leads. Nothing is sent until you connect a key and tick a form. The plugin is also at https://app.arraxis.site/wp-plugin/arraxis-leads/arraxis-leads.zip.

The same reference in machine-readable form: openapi.json (OpenAPI 3.1).