axoCRM API
The axoCRM REST API lets you create contacts, companies, deals and tasks from external systems. API access and webhooks are available on the Paid plan.
Base URL
https://www.axocrm.com/api/v1Authentication
All requests require an API key in the Authorization header. Create keys in Settings → API keys inside your workspace. The raw key is shown only once at creation — store it securely.
Authorization: Bearer axo_live_xxxxxxxxxxxxxxxxxxxxError format
{
"error": {
"code": "validation_error",
"message": "Human readable message"
}
}Error codes: unauthorized, forbidden, not_found, validation_error, limit_reached, rate_limited, server_error.
Successful responses include X-RateLimit-Limit and X-RateLimit-Remaining headers showing the per-workspace budget for the current rolling 24-hour window.
Endpoints
GET /api/v1/me
Returns the workspace this API key belongs to and available scopes.
curl https://www.axocrm.com/api/v1/me \
-H "Authorization: Bearer $AXO_API_KEY"POST /api/v1/contacts
Create a contact. At least one of first_name, last_name or email is required.
curl -X POST https://www.axocrm.com/api/v1/contacts \
-H "Authorization: Bearer $AXO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"first_name": "Jane",
"last_name": "Doe",
"email": "jane@example.com",
"source": "website"
}'POST /api/v1/companies
Create a company. name is required.
curl -X POST https://www.axocrm.com/api/v1/companies \
-H "Authorization: Bearer $AXO_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "name": "Acme Ltd", "website": "https://acme.example" }'POST /api/v1/deals
Create a deal. name is required. If pipeline_id/stage_id are omitted, the default pipeline and first stage are used.
curl -X POST https://www.axocrm.com/api/v1/deals \
-H "Authorization: Bearer $AXO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Acme — Q3 contract",
"value": 5000,
"currency": "GBP"
}'POST /api/v1/tasks
Create a task. title is required.
curl -X POST https://www.axocrm.com/api/v1/tasks \
-H "Authorization: Bearer $AXO_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "title": "Follow up with Acme", "priority": "high" }'Webhooks
Subscribe to events from Settings → Webhooks. Available on the Paid plan. The destination URL must use HTTPS. axoCRM POSTs a JSON payload with these headers:
X-axoCRM-Event— event name (e.g.contact.created)X-axoCRM-Timestamp— unix timestamp (seconds)X-axoCRM-Signature—sha256=<hex>HMAC overtimestamp.body
Event types: contact.created, company.created, deal.created, deal.updated, deal.won, deal.lost, task.created, task.completed, lead_form.submitted, webhook.test.
Events fire from API-created records, records created or updated in the app UI, and from public lead form submissions. Lead form submissions emit a single lead_form.submitted event (not separate contact.created / company.created / deal.created events) to avoid noisy delivery. webhook.test is only sent when an admin clicks Send test in Settings → Webhooks.
Event filtering
Each subscription chooses which event types it receives. Edit the selection from Settings → Webhooks → Edit events. Filtering is enforced server-side when events are enqueued — subscriptions only receive events they have selected, and paused/revoked subscriptions never receive normal events. Changes affect future events only; already-queued deliveries are unchanged. webhook.test is sent via the explicit Send test action and is not part of the normal filter set.
Payload shape
{
"id": "evt_...",
"type": "contact.created",
"workspace_id": "...",
"created_at": "2026-06-01T12:00:00.000Z",
"data": {
"object": {
"type": "contact",
"id": "...",
"status": "lead",
"source": "website",
"has_email": true,
"has_phone": false,
"has_company": true
}
}
}Payloads intentionally exclude private CRM content — no contact names, emails, phones, notes, task descriptions, company names, or custom field values. Use the API with the included id to fetch full records when authorised. Example payload shapes for every event type are available in-app at Settings → Webhooks → Example payload shapes (placeholder values only — no real CRM data).
Signing secret
Each subscription has a signing secret shown once at creation. Store it securely on your receiver — axoCRM cannot show it again. The signature is computed as HMAC-SHA256(secret, "{timestamp}.{body}") and sent hex-encoded in the X-axoCRM-Signature header prefixed with sha256=. axoCRM stores the secret encrypted at rest (AES-256-GCM) and uses it directly to sign outbound deliveries, so the value you were shown is the value you verify with.
Workspace owners, admins, and managers can rotate the signing secret at any time from Settings → Webhooks → Rotate secret. The new secret is shown once. After rotation, new deliveries are signed with the new secret only — update your receiver's verification secret immediately to avoid signature failures. axoCRM does not sign with both old and new secrets; there is no dual-verification window.
Verify a signature (Node.js)
import crypto from "crypto";
function verify(rawBody, headers, secret) {
const ts = headers["x-axocrm-timestamp"];
const sig = (headers["x-axocrm-signature"] || "").replace("sha256=", "");
const expected = crypto.createHmac("sha256", secret)
.update(`${ts}.${rawBody}`).digest("hex");
return sig.length === expected.length &&
crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected));
}For best practice, also reject deliveries whose X-axoCRM-Timestamp is more than a few minutes old to prevent replay attacks.
Rate limits
1,000 requests per rolling 24 hours per workspace on the Paid plan. Requests over the limit return 429 rate_limited with a Retry-After header. The limit applies to the workspace as a whole — using multiple API keys in the same workspace does not raise it.
Key usage visibility
Workspace owners and admins can see safe usage metadata per key in Settings → API keys: last-used timestamp, request count over the last 24 hours and 7 days, server-error count, and rate-limited count. The dashboard never exposes request bodies, Authorization headers, or raw key values — those are not stored. Revoked keys stop working immediately and remain visible for audit.
Key expiry
API keys can optionally expire. When creating a key in Settings → API keys, choose No expiry, a fixed window (7, 30, or 90 days), or a custom future date. Owners and admins can also update or remove the expiry of an existing key. Once expires_at is reached, authentication fails with 401 api_key_expired and the key shows as Expired in settings. Expired keys are not deleted automatically and can still be revoked. Keys within 7 days of expiry are flagged as Expiring soon. Raw keys are still shown only once at creation; rotate by creating a new key and revoking the old one.
Notes & limitations
- API and webhooks are Paid-plan features. Free workspaces receive
403 forbidden. - Webhook deliveries are enqueued when an event fires. Initial delivery and retries are processed when the delivery runner is invoked — manually from Settings → Webhooks → Send pending now (or per-subscription Send test), or by an external scheduler (cron) calling the delivery server function on a 1–5 minute interval. Fully automatic scheduled retries are not yet enabled — see
docs/webhook-operations.md. - Failed deliveries are retried with exponential backoff up to 5 attempts.
- Payloads contain safe summary fields only — see Payload shape above.
- Signing secrets can be rotated in-place from the webhooks settings page; the new secret is shown once and immediately replaces the old one for outbound signing.