Read and change your CRM data from your own systems. Base address: https://crm.xonik.com/api/v1. Machine-readable description: openapi.json.
In XONIKA3 go to Admin → Developers, create a key and choose what it may do. The key is shown once. Send it on every request:
curl https://crm.xonik.com/api/v1/crm/contacts \ -H "Authorization: Bearer xk_live_…"
A key can never do more than the person who made it can do today, and it stops working if they leave the organization. Keys cannot sign in, manage people, billing, integrations or other keys. Limit: 600 requests a minute per key.
| Permission | Allows |
|---|---|
crm.contact:read | Read contacts and companies |
crm.contact:create | Create contacts and companies |
crm.contact:update | Update contacts and companies |
crm.deal:read | Read deals, products, tasks and notes |
crm.deal:create | Create deals, tasks and notes |
crm.deal:update | Update deals and tasks |
prospecting.lead:read | Read leads |
prospecting.lead:create | Create leads |
support.ticket:read | Read tickets |
support.ticket:create | Create tickets |
support.ticket:update | Reply to and update tickets |
invoicing.invoice:read | Read invoices and payments |
invoicing.invoice:create | Create invoices |
commerce.order:read | Read orders |
commerce.order:create | Create orders |
booking.reservation:read | Read bookings |
value_cents: 150000 = 1,500.00.{ "statusCode": 409, "code": "OVERPAYMENT", "message": "…" } — the message is written to be shown to people./crm/contactsList contacts · permission crm.contact:read
Query: search Name, email or company · page Page number · limit Per page (max 100)
/crm/contactsCreate a contact · permission crm.contact:create
{
"first_name": {
"type": "string"
},
"last_name": {
"type": "string"
},
"email": {
"type": "string",
"format": "email"
},
"phone": {
"type": "string"
},
"company_name": {
"type": "string"
},
"title": {
"type": "string"
},
"city": {
"type": "string"
},
"country": {
"type": "string"
},
"owner_id": {
"type": "string",
"format": "uuid"
},
"custom_fields": {
"type": "object"
}
}Required: first_name
/crm/contacts/{id}Get a contact · permission crm.contact:read
/crm/contacts/{id}Update a contact (only the fields sent) · permission crm.contact:update
{
"first_name": {
"type": "string"
},
"last_name": {
"type": "string"
},
"email": {
"type": "string"
},
"phone": {
"type": "string"
},
"title": {
"type": "string"
},
"company_name": {
"type": "string"
},
"status": {
"type": "string"
},
"lifecycle_stage": {
"type": "string"
},
"owner_id": {
"type": "string",
"format": "uuid"
}
}/crm/companiesList companies · permission crm.contact:read
/crm/companiesCreate a company · permission crm.contact:create
{
"name": {
"type": "string"
},
"domain": {
"type": "string"
},
"industry": {
"type": "string"
},
"size": {
"type": "string"
},
"country": {
"type": "string"
},
"city": {
"type": "string"
}
}Required: name
/crm/companies/{id}Update a company · permission crm.contact:update
{
"name": {
"type": "string"
},
"domain": {
"type": "string"
},
"industry": {
"type": "string"
},
"tier": {
"type": "string"
}
}/crm/dealsList deals · permission crm.deal:read
Query: stage lead, qualified, proposal, negotiation, won, lost · owner_id Owner
/crm/dealsCreate a deal · permission crm.deal:create
{
"title": {
"type": "string"
},
"value_cents": {
"type": "integer",
"description": "Amount in the smallest unit, e.g. 150000 = 1,500.00"
},
"currency": {
"type": "string",
"description": "ISO code; default: the organization's"
},
"stage": {
"type": "string"
},
"contact_id": {
"type": "string",
"format": "uuid"
},
"company_id": {
"type": "string",
"format": "uuid"
},
"owner_id": {
"type": "string",
"format": "uuid"
},
"expected_close_date": {
"type": "string",
"format": "date-time"
}
}Required: title, value_cents
/crm/deals/{id}Get a deal · permission crm.deal:read
/crm/deals/{id}/moveMove a deal to another stage (lost needs loss_reason) · permission crm.deal:update
{
"stage_key": {
"type": "string",
"description": "A stage key of the deal's pipeline, e.g. won"
},
"pipeline_id": {
"type": "string",
"format": "uuid"
},
"loss_reason": {
"type": "string",
"description": "price, feature_gap, competitor, timing, …"
}
}Required: stage_key
/crm/productsList products · permission crm.deal:read
/crm/tasksList follow-ups · permission crm.deal:read
/crm/tasksCreate a follow-up · permission crm.deal:create
{
"title": {
"type": "string"
},
"due_date": {
"type": "string",
"format": "date-time"
},
"priority": {
"type": "string",
"description": "low, medium, high, urgent"
},
"contact_id": {
"type": "string",
"format": "uuid"
},
"deal_id": {
"type": "string",
"format": "uuid"
},
"assigned_to": {
"type": "string",
"format": "uuid"
}
}Required: title
/crm/notesAdd a note to a contact or deal · permission crm.deal:create
{
"content": {
"type": "string"
},
"contact_id": {
"type": "string",
"format": "uuid"
},
"deal_id": {
"type": "string",
"format": "uuid"
}
}Required: content
/prospecting/leadsList leads · permission prospecting.lead:read
/prospecting/leadsCreate a lead · permission prospecting.lead:create
{
"company_name": {
"type": "string"
},
"contact_name": {
"type": "string"
},
"email": {
"type": "string"
},
"domain": {
"type": "string"
},
"score": {
"type": "integer",
"description": "0-100"
}
}Required: company_name
/support/ticketsList tickets · permission support.ticket:read
Query: status open, pending, resolved, closed · priority low, medium, high, urgent
/support/ticketsOpen a ticket · permission support.ticket:create
{
"subject": {
"type": "string"
},
"description": {
"type": "string"
},
"priority": {
"type": "string"
},
"customer_email": {
"type": "string",
"description": "Matched to (or added as) a contact"
},
"customer_id": {
"type": "string",
"format": "uuid"
}
}Required: subject, description
/support/tickets/{id}Get a ticket with its messages · permission support.ticket:read
/support/tickets/{id}/messagesAdd a reply or internal note · permission support.ticket:update
{
"body": {
"type": "string"
},
"is_internal_note": {
"type": "boolean"
}
}Required: body
/invoicing/invoicesList invoices · permission invoicing.invoice:read
Query: status draft, issued, partially_paid, paid, credited, void
/invoicing/invoicesCreate an invoice (issued unless status is draft) · permission invoicing.invoice:create
{
"customer_name": {
"type": "string"
},
"customer_email": {
"type": "string"
},
"customer_contact_id": {
"type": "string",
"format": "uuid"
},
"currency": {
"type": "string"
},
"due_at": {
"type": "string",
"format": "date-time"
},
"status": {
"type": "string",
"description": "draft or issued"
},
"items": {
"type": "array",
"items": {
"type": "object",
"properties": {
"description": {
"type": "string"
},
"quantity": {
"type": "integer"
},
"unit_price_cents": {
"type": "integer"
},
"tax_rate_percent": {
"type": "number"
}
},
"required": [
"description",
"quantity",
"unit_price_cents"
]
}
}
}Required: items
/invoicing/invoices/{id}Get an invoice with lines, payments, credit notes and refunds · permission invoicing.invoice:read
/commerce/ordersList orders · permission commerce.order:read
/commerce/ordersCreate an order · permission commerce.order:create
{
"customer_id": {
"type": "string",
"format": "uuid"
},
"items": {
"type": "array",
"items": {
"type": "object",
"properties": {
"description": {
"type": "string"
},
"quantity": {
"type": "integer"
},
"unit_price_cents": {
"type": "integer"
},
"product_id": {
"type": "string",
"format": "uuid"
}
},
"required": [
"description",
"quantity",
"unit_price_cents"
]
}
},
"tax_rate_percent": {
"type": "number"
},
"discount_code": {
"type": "string"
}
}Required: items
/commerce/orders/{id}Get an order · permission commerce.order:read
In Admin → Developers → Webhooks add an https address and choose events. Each delivery is a POST with JSON:
{ "id": "evt_…", "type": "deal.won", "created_at": "2026-10-28T10:00:00Z", "data": { "object": "deal", "id": "…", "title": "…", "value_cents": 150000, … } }
Check it came from XONIKA3: the header X-Xonika-Signature: t=<unix time>,v1=<hex> is the HMAC-SHA256 of <t>.<raw body> with your webhook's signing secret. Reject old timestamps (over 5 minutes). Answer with any 2xx within 10 seconds; otherwise it is retried after 1 min, 5 min, 30 min, 2 h, 12 h and 24 h. The same event may arrive twice: use id to ignore repeats. A webhook that fails 25 times in a row is paused (and the team is told).
// Node.js
const [t, v1] = header.split(',').map((p) => p.split('=')[1]);
const expected = crypto.createHmac('sha256', secret).update(t + '.' + rawBody).digest('hex');
const valid = crypto.timingSafeEqual(Buffer.from(v1), Buffer.from(expected)) && Math.abs(Date.now() / 1000 - Number(t)) < 300;
| Event | When | data.object |
|---|---|---|
contact.created | A contact is added | contact |
contact.updated | A contact is changed | contact |
deal.created | A deal is added | deal |
deal.stage_changed | A deal moves stage | deal |
deal.won | A deal is won | deal |
deal.lost | A deal is lost | deal |
lead.created | A lead is added | lead |
ticket.created | A ticket is opened | ticket |
ticket.resolved | A ticket is resolved or closed | ticket |
invoice.issued | An invoice is issued | invoice |
invoice.paid | An invoice is paid in full | invoice |
invoice.overdue | An invoice becomes overdue | invoice |
payment.received | A payment is recorded | invoice |
order.created | An order is placed | order |
order.paid | An order is paid | order |
booking.created | A meeting is booked | booking |
booking.cancelled | A meeting is cancelled | booking |
form.submitted | A form is submitted | form_submission |