{"info":{"name":"Labeeq API","description":"The Labeeq API lets you build on top of your own workspace: read contacts and\nconversations, send a real message on any connected channel, and open deals in\nyour pipeline. It is a JSON REST API over HTTPS, included in every plan.\n\nRather click around first? Import the [Postman collection](https://developers.labeeq.com/postman_collection.json) — every\nendpoint below, with example bodies and the auth header already pointed at a\n`token` variable. It is generated from this document, so the two cannot disagree.\n\n## Capability availability\n\n| Capability | Web | Developer API | Mobile API | Connectors |\n| --- | --- | --- | --- | --- |\n| جهات الاتصال | جهات الاتصال | Available | Available | عبر واجهة المطور |\n| رسائل العملاء | صندوق الوارد | Available | Available | عبر واجهة المطور |\n| الطلبات | الطلبات | Available | Available | عبر واجهة المطور |\n| اكتشاف معرّفات مساحة العمل | إعدادات مساحة العمل | Unavailable | Available | غير متاح عبر الواجهة العامة |\n| تعديل الحقول المخصصة | جهات الاتصال | Unavailable | Unavailable | غير متاح عبر الواجهة العامة |\n| بدء سير عمل | صندوق الوارد | Unavailable | Unavailable | غير متاح عبر الواجهة العامة |\n| إرسال حملة | Campaigns | Unavailable | Unavailable | غير متاح عبر الواجهة العامة |\n| إدارة اشتراكات إشعارات الويب | Integrations | Unavailable | Unavailable | غير متاح عبر الواجهة العامة |\n\nAvailability is subject to workspace permissions and plan limits. Mobile API entries\ndescribe the backend contract, not a guarantee of a corresponding screen in every app version.\nOutbound webhook events can be configured in the web application even when webhook\nsubscription management is unavailable over the Developer API. Connectors cannot bypass\ndisabled endpoints. Google Sheets and Meta Conversions availability is shown in the\nintegration hub; a Coming soon connector is not a supported customer workflow.\n\n## Authentication\n\nEvery request carries a personal access token as a bearer token:\n\n```\nAuthorization: Bearer YOUR_TOKEN\n```\n\nCreate tokens in **Settings → Integrations → Developer API**. The plain token is\nshown once, at creation — only its hash is stored, so a lost token is replaced,\nnever recovered. A token may carry an **expiry date**; the workspace's owners are\nemailed a week before it, and an expired token answers `401` with\n`code: token_expired`. **Rotate** it from the same page: the replacement keeps the\nname, abilities, allowlist and lifetime, and the old key can be left working for\na short grace period while you swap it in.\n\nA token belongs in the header and nowhere else. Requests carrying a `token` or\n`api_token` **query parameter are refused with `400`**, because a token in a URL\nis copied into access logs, proxy caches and browser history. Requests over plain\nHTTP are refused with `403`; every call must be TLS.\n\nCall `GET /me` first: it answers with the workspace the token belongs to and the\nabilities it carries, which is the quickest way to confirm your setup.\n\nA token can also be **pinned to the addresses it may be used from** — an exact\naddress or a CIDR range, set when you create it. Used from anywhere else it\nanswers `403`, which makes a leaked token far less useful to whoever found it.\nEvery write your tokens make is recorded with its token, address and result, and\nthe trail is visible in the same settings page.\n\n## Abilities\n\nA token carries a fixed set of abilities, chosen when you create it. **An ability\ncan only narrow what its owner may already do** — the call must pass *both* the\ntoken's abilities and the owning teammate's own role permissions. Revoking a\nteammate's access therefore revokes their tokens' reach at the same time.\n\n| Ability | Grants |\n| --- | --- |\n| `contacts:read` | قراءة جهات الاتصال |\n| `contacts:write` | إنشاء وتعديل جهات الاتصال |\n| `contacts:delete` | حذف جهات الاتصال |\n| `conversations:read` | قراءة المحادثات |\n| `conversations:write` | فتح وإغلاق وإسناد وتوجيه المحادثات |\n| `messages:send` | إرسال الرسائل |\n| `deals:read` | قراءة الصفقات |\n| `deals:write` | إنشاء الصفقات وتعديلها |\n| `orders:read` | قراءة الطلبات |\n| `orders:write` | إنشاء الطلبات وتعديلها ونقلها |\n| `products:read` | قراءة كتالوج المنتجات |\n| `tasks:read` | قراءة المهام |\n| `tasks:write` | إنشاء المهام وتعديلها |\n\n## Workspaces\n\nA token is bound to the workspace of the teammate who created it. Every response is\nscoped to that workspace: a record belonging to another one answers `404`, never\nanother company's data.\n\n## Pagination\n\nCollections are cursor-paginated. Follow `meta.next_cursor` and pass it back as\n`?cursor=`; `per_page` defaults to 50 and caps at 100. Cursors are opaque — treat\nthem as strings and do not construct them yourself.\n\n## Rate limits\n\nTwo limits apply, and **both announce themselves on every response** so you can slow\ndown before you are cut off rather than discovering the wall by hitting it.\n\n| Header | Means |\n| --- | --- |\n| `X-RateLimit-Limit` / `X-RateLimit-Remaining` | The **burst** limit — 30, 60 or 100 requests per 10 seconds, by plan. |\n| `X-Quota-Limit` / `X-Quota-Remaining` | The **month's** allowance of total calls, by plan. |\n| `X-Quota-Reset` | When that allowance resets — the start of next month. |\n\nExceeding either answers `429` with a `Retry-After` in seconds. The difference matters:\na burst clears in seconds, while the monthly allowance is spent for the **rest of the\nmonth**, so `Retry-After` there points at next month rather than a moment away. Plans\nwithout a monthly cap send no quota headers rather than inventing an infinity.\n\nBack off and retry rather than hammering — the burst counter is per token, so a\nrunaway script starves your other integrations too.\n\n## Retrying a write safely\n\nA timeout tells you nothing about whether the work happened. Send an\n`Idempotency-Key` header on any `POST` — any unique string of your own, an order\nid or a UUID — and retrying with the same key returns the **first** response\ninstead of sending a second message or opening a second deal.\n\n```\nIdempotency-Key: order-4821-shipped\n```\n\nA replayed response carries `Idempotent-Replay: true`. Keys are remembered for 24\nhours and are scoped to your workspace, so they cannot collide with another\ncustomer's. Reusing a key with a *different* body answers `422` — that is a bug\non your side, and returning the earlier result would hide it. A request that\nfailed is not recorded, so you can fix the body and retry with the same key.\nSending no key keeps the old behaviour: every call does the work.\n\nNot every `POST` is a write: searching contacts asks a question in a body and\nhonours no key. Operations that accept one document the `Idempotency-Key` header;\nthe rest do not.\n\n## Events and no-code automation\n\nThis API is for reading and writing your workspace directly. If you want events\npushed to you the moment something happens, or automation without writing code,\nuse the **Zapier integration** (Settings → Integrations → Zapier) — it carries the\nsame message, conversation and contact events and needs no polling.\n\nEndpoints of your own subscribe in **Settings → Integrations → Webhooks**. Every\nevent they can receive is listed under **Webhooks** in this reference, with the\nexact payload it carries: the same resource shapes the API returns.\n\nThe Zapier and n8n pages issue their keys as ordinary **scoped tokens**: preset to\nread and write contacts, conversations, messages, deals and tasks, and listed with\nyour other tokens, where they can be pinned, rotated and revoked. Keys those pages\nmade before scoped tokens carry every ability; they keep working, and the pages\noffer to switch them. If the platform ever sets a retirement date for them, a call\nwith such a key after that date answers `403` with `code` `legacy_key_retired`.\n\n## Errors\n\nEvery error is JSON of the same shape — a `message`, plus an `errors` map when a\nbody failed validation — so one handler covers them all. Internal details are\nnever returned: an unexpected failure is a plain `500`, with nothing about the\nframework or the database in it.\n\n| Code | What it means | What to do |\n| --- | --- | --- |\n| `400` | The request itself was malformed — a token in the query string, for instance. | Fix the request; retrying will not help. |\n| `401` | No token, or one that has been revoked. When the token exists but is past its date, `code` is `token_expired`. | Rotate the token in Settings → Integrations — the replacement keeps its abilities and allowlist, so only the key changes. |\n| `402` | A plan allowance is spent — sending a campaign beyond the month's broadcast limit. | Upgrade, or wait for the month to turn. |\n| `403` | The token lacks the ability, **or** its teammate lacks the permission — both must allow the call. Also answered when the call was not over HTTPS, and with `code` `legacy_key_retired` for a retired full-access Zapier or n8n key. | Check the ability list above, then the teammate's role. |\n| `404` | No such record **in this workspace**. Another company's record answers this too, never their data. | Check the id, and that it belongs to you. |\n| `409` | A request with this `Idempotency-Key` is still in flight. | Wait a moment and retry with the same key. |\n| `422` | The body failed validation, or a value names something that does not exist — a label, a stage, a template. | Read `errors`; it names the field and what was allowed. |\n| `429` | Rate limited — burst or monthly. | Honour `Retry-After`. |\n\n**A `422` is usually a refusal, not a bug.** This API declines to guess: a search\nrule it cannot read, a label the workspace does not have, a template that exists in\ntwo languages, a merge missing one of its two sides — each says so and names the\nfield, rather than doing something plausible and leaving you to discover it later.","schema":"https://schema.getpostman.com/json/collection/v2.1.0/collection.json"},"auth":{"type":"bearer","bearer":[{"key":"token","value":"{{token}}","type":"string"}]},"variable":[{"key":"base_url","value":"https://developers.labeeq.com/api/v1","type":"string","description":"Every path is relative to this."},{"key":"token","value":"","type":"string","description":"A personal access token from Settings → Integrations → Developer API. Shown once, at creation."},{"key":"contact_id","value":"","type":"string","description":"The id used by the requests that address a single record."},{"key":"label","value":"","type":"string","description":"The id used by the requests that address a single record."},{"key":"conversation_id","value":"","type":"string","description":"The id used by the requests that address a single record."},{"key":"message_id","value":"","type":"string","description":"The id used by the requests that address a single record."},{"key":"deal_id","value":"","type":"string","description":"The id used by the requests that address a single record."},{"key":"order_id","value":"","type":"string","description":"The id used by the requests that address a single record."},{"key":"product_id","value":"","type":"string","description":"The id used by the requests that address a single record."},{"key":"task_id","value":"","type":"string","description":"The id used by the requests that address a single record."}],"item":[{"name":"Account","description":"Who the token belongs to.","item":[{"name":"Who am I","request":{"method":"GET","header":[{"key":"Accept","value":"application/json"}],"url":{"raw":"{{base_url}}/me","host":["{{base_url}}"],"path":["me"]},"description":"The token's teammate, their workspace, and the abilities the token carries. Costs nothing and touches no data — the call to make first."},"response":[{"name":"OK","originalRequest":{"method":"GET","header":[{"key":"Accept","value":"application/json"}],"url":{"raw":"{{base_url}}/me","host":["{{base_url}}"],"path":["me"]},"description":"The token's teammate, their workspace, and the abilities the token carries. Costs nothing and touches no data — the call to make first."},"status":"OK","code":200,"header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n    \"data\": {\n        \"user\": {\n            \"id\": 7,\n            \"name\": \"Mona Zaki\",\n            \"email\": \"mona@hadayek.com\"\n        },\n        \"company\": {\n            \"id\": 3,\n            \"name\": \"Hadayek Clinics\"\n        },\n        \"account\": {\n            \"id\": 2,\n            \"name\": \"Hadayek Group\"\n        },\n        \"abilities\": [\n            \"contacts:read\",\n            \"messages:send\"\n        ]\n    }\n}","_postman_previewlanguage":"json"}]}]},{"name":"Contacts","description":"The people who talk to you — one record per person, across every channel.","item":[{"name":"List contacts","request":{"method":"GET","header":[{"key":"Accept","value":"application/json"}],"url":{"raw":"{{base_url}}/contacts","host":["{{base_url}}"],"path":["contacts"],"query":[{"key":"search","value":"","description":"Matches name and email (and phone, when the token's teammate may see phone numbers).","disabled":true},{"key":"email","value":"","description":"An exact email address, compared case-insensitively.","disabled":true},{"key":"phone","value":"","description":"A phone number in any spelling — matched on its last nine digits. Answers `403` without the view-phone permission.","disabled":true},{"key":"label","value":"","description":"A label id, or its exact name.","disabled":true},{"key":"lifecycle_stage_id","value":"","description":"Contacts sitting on one stage of the lifecycle.","disabled":true},{"key":"created_after","value":"","description":"Contacts added at or after this moment. Any ISO-8601 date or timestamp.","disabled":true},{"key":"created_before","value":"","description":"Contacts added at or before this moment.","disabled":true},{"key":"updated_after","value":"","description":"Contacts changed at or after this moment — the parameter a sync job polls with.","disabled":true},{"key":"include","value":"","description":"Extra blocks to return with each contact, comma-separated: `labels`, `lifecycle`. Unknown names are ignored.","disabled":true},{"key":"per_page","value":"","description":"Rows per page. Defaults to 50, capped at 100.","disabled":true},{"key":"cursor","value":"","description":"The `meta.next_cursor` from the previous page. Opaque — pass it back unchanged.","disabled":true}]},"description":"Contacts in the workspace, newest first. `search` matches name and email; it also matches phone numbers when the token's teammate may see them. Filters combine: every one you send must match.\n\n**Required ability:** `contacts:read`"},"response":[{"name":"OK","originalRequest":{"method":"GET","header":[{"key":"Accept","value":"application/json"}],"url":{"raw":"{{base_url}}/contacts","host":["{{base_url}}"],"path":["contacts"],"query":[{"key":"search","value":"","description":"Matches name and email (and phone, when the token's teammate may see phone numbers).","disabled":true},{"key":"email","value":"","description":"An exact email address, compared case-insensitively.","disabled":true},{"key":"phone","value":"","description":"A phone number in any spelling — matched on its last nine digits. Answers `403` without the view-phone permission.","disabled":true},{"key":"label","value":"","description":"A label id, or its exact name.","disabled":true},{"key":"lifecycle_stage_id","value":"","description":"Contacts sitting on one stage of the lifecycle.","disabled":true},{"key":"created_after","value":"","description":"Contacts added at or after this moment. Any ISO-8601 date or timestamp.","disabled":true},{"key":"created_before","value":"","description":"Contacts added at or before this moment.","disabled":true},{"key":"updated_after","value":"","description":"Contacts changed at or after this moment — the parameter a sync job polls with.","disabled":true},{"key":"include","value":"","description":"Extra blocks to return with each contact, comma-separated: `labels`, `lifecycle`. Unknown names are ignored.","disabled":true},{"key":"per_page","value":"","description":"Rows per page. Defaults to 50, capped at 100.","disabled":true},{"key":"cursor","value":"","description":"The `meta.next_cursor` from the previous page. Opaque — pass it back unchanged.","disabled":true}]},"description":"Contacts in the workspace, newest first. `search` matches name and email; it also matches phone numbers when the token's teammate may see them. Filters combine: every one you send must match.\n\n**Required ability:** `contacts:read`"},"status":"OK","code":200,"header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n    \"data\": [\n        {\n            \"id\": 42,\n            \"name\": \"Mona Zaki\",\n            \"phone\": \"+201001234567\",\n            \"email\": \"mona@example.com\",\n            \"lifecycle_stage_id\": 3,\n            \"blocked\": false,\n            \"created_at\": \"2026-07-26T09:41:00+00:00\",\n            \"updated_at\": \"2026-07-26T09:41:00+00:00\"\n        }\n    ],\n    \"meta\": {\n        \"next_cursor\": null,\n        \"per_page\": 50\n    }\n}","_postman_previewlanguage":"json"}]},{"name":"Create a contact","request":{"method":"POST","header":[{"key":"Accept","value":"application/json"},{"key":"Content-Type","value":"application/json"},{"key":"Idempotency-Key","value":"{{$guid}}","description":"Any unique string of your own. Retrying with the same key returns the first response instead of repeating the work. Remembered for 24 hours."}],"url":{"raw":"{{base_url}}/contacts","host":["{{base_url}}"],"path":["contacts"]},"description":"Adds a person to the workspace. A contact needs a name; give a phone or an email so you can reach them.\n\n**Required ability:** `contacts:write`","body":{"mode":"raw","raw":"{\n    \"name\": \"Mona Zaki\",\n    \"email\": \"mona@example.com\",\n    \"phone\": \"+201001234567\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"Created","originalRequest":{"method":"POST","header":[{"key":"Accept","value":"application/json"},{"key":"Content-Type","value":"application/json"},{"key":"Idempotency-Key","value":"{{$guid}}","description":"Any unique string of your own. Retrying with the same key returns the first response instead of repeating the work. Remembered for 24 hours."}],"url":{"raw":"{{base_url}}/contacts","host":["{{base_url}}"],"path":["contacts"]},"description":"Adds a person to the workspace. A contact needs a name; give a phone or an email so you can reach them.\n\n**Required ability:** `contacts:write`","body":{"mode":"raw","raw":"{\n    \"name\": \"Mona Zaki\",\n    \"email\": \"mona@example.com\",\n    \"phone\": \"+201001234567\"\n}","options":{"raw":{"language":"json"}}}},"status":"Created","code":201,"header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n    \"data\": {\n        \"id\": 42,\n        \"name\": \"Mona Zaki\",\n        \"phone\": \"+201001234567\",\n        \"email\": \"mona@example.com\",\n        \"lifecycle_stage_id\": 3,\n        \"blocked\": false,\n        \"created_at\": \"2026-07-26T09:41:00+00:00\",\n        \"updated_at\": \"2026-07-26T09:41:00+00:00\"\n    }\n}","_postman_previewlanguage":"json"}]},{"name":"Get a contact","request":{"method":"GET","header":[{"key":"Accept","value":"application/json"}],"url":{"raw":"{{base_url}}/contacts/{{contact_id}}","host":["{{base_url}}"],"path":["contacts","{{contact_id}}"],"query":[{"key":"include","value":"","description":"Extra blocks to return with each contact, comma-separated: `labels`, `lifecycle`. Unknown names are ignored.","disabled":true}]},"description":"One contact, addressed by id or by something you already hold — see the `id` parameter. A contact in another workspace answers `404`.\n\n**Required ability:** `contacts:read`"},"response":[{"name":"OK","originalRequest":{"method":"GET","header":[{"key":"Accept","value":"application/json"}],"url":{"raw":"{{base_url}}/contacts/{{contact_id}}","host":["{{base_url}}"],"path":["contacts","{{contact_id}}"],"query":[{"key":"include","value":"","description":"Extra blocks to return with each contact, comma-separated: `labels`, `lifecycle`. Unknown names are ignored.","disabled":true}]},"description":"One contact, addressed by id or by something you already hold — see the `id` parameter. A contact in another workspace answers `404`.\n\n**Required ability:** `contacts:read`"},"status":"OK","code":200,"header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n    \"data\": {\n        \"id\": 42,\n        \"name\": \"Mona Zaki\",\n        \"phone\": \"+201001234567\",\n        \"email\": \"mona@example.com\",\n        \"lifecycle_stage_id\": 3,\n        \"blocked\": false,\n        \"created_at\": \"2026-07-26T09:41:00+00:00\",\n        \"updated_at\": \"2026-07-26T09:41:00+00:00\"\n    }\n}","_postman_previewlanguage":"json"}]},{"name":"Update a contact","request":{"method":"PATCH","header":[{"key":"Accept","value":"application/json"},{"key":"Content-Type","value":"application/json"},{"key":"Idempotency-Key","value":"{{$guid}}","description":"Any unique string of your own. Retrying with the same key returns the first response instead of repeating the work. Remembered for 24 hours."}],"url":{"raw":"{{base_url}}/contacts/{{contact_id}}","host":["{{base_url}}"],"path":["contacts","{{contact_id}}"]},"description":"Changes the fields you send and leaves the rest alone — sending only `email` will not empty the name. Moving `lifecycle_stage_id` runs the same rules the panel does, so open deals settle with the stage.\n\n**Required ability:** `contacts:write`","body":{"mode":"raw","raw":"{\n    \"name\": \"Mona Zaki\",\n    \"email\": \"mona@example.com\",\n    \"phone\": \"+201001234567\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PATCH","header":[{"key":"Accept","value":"application/json"},{"key":"Content-Type","value":"application/json"},{"key":"Idempotency-Key","value":"{{$guid}}","description":"Any unique string of your own. Retrying with the same key returns the first response instead of repeating the work. Remembered for 24 hours."}],"url":{"raw":"{{base_url}}/contacts/{{contact_id}}","host":["{{base_url}}"],"path":["contacts","{{contact_id}}"]},"description":"Changes the fields you send and leaves the rest alone — sending only `email` will not empty the name. Moving `lifecycle_stage_id` runs the same rules the panel does, so open deals settle with the stage.\n\n**Required ability:** `contacts:write`","body":{"mode":"raw","raw":"{\n    \"name\": \"Mona Zaki\",\n    \"email\": \"mona@example.com\",\n    \"phone\": \"+201001234567\"\n}","options":{"raw":{"language":"json"}}}},"status":"OK","code":200,"header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n    \"data\": {\n        \"id\": 42,\n        \"name\": \"Mona Zaki\",\n        \"phone\": \"+201001234567\",\n        \"email\": \"mona@example.com\",\n        \"lifecycle_stage_id\": 3,\n        \"blocked\": false,\n        \"created_at\": \"2026-07-26T09:41:00+00:00\",\n        \"updated_at\": \"2026-07-26T09:41:00+00:00\"\n    }\n}","_postman_previewlanguage":"json"}]},{"name":"Create or update a contact","request":{"method":"PUT","header":[{"key":"Accept","value":"application/json"},{"key":"Content-Type","value":"application/json"},{"key":"Idempotency-Key","value":"{{$guid}}","description":"Any unique string of your own. Retrying with the same key returns the first response instead of repeating the work. Remembered for 24 hours."}],"url":{"raw":"{{base_url}}/contacts/{{contact_id}}","host":["{{base_url}}"],"path":["contacts","{{contact_id}}"]},"description":"Makes the contact at this address look like the body, creating it when there is none — the call a sync loop makes when it holds an email or a phone and does not know whether you have the person already. Answers `201` when it created and `200` when it updated.\n\nAddressing by `id:` never creates: an id is ours to issue, so one we never gave out answers `404`.\n\n**Required ability:** `contacts:write`","body":{"mode":"raw","raw":"{\n    \"name\": \"Mona Zaki\",\n    \"email\": \"mona@example.com\",\n    \"phone\": \"+201001234567\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","header":[{"key":"Accept","value":"application/json"},{"key":"Content-Type","value":"application/json"},{"key":"Idempotency-Key","value":"{{$guid}}","description":"Any unique string of your own. Retrying with the same key returns the first response instead of repeating the work. Remembered for 24 hours."}],"url":{"raw":"{{base_url}}/contacts/{{contact_id}}","host":["{{base_url}}"],"path":["contacts","{{contact_id}}"]},"description":"Makes the contact at this address look like the body, creating it when there is none — the call a sync loop makes when it holds an email or a phone and does not know whether you have the person already. Answers `201` when it created and `200` when it updated.\n\nAddressing by `id:` never creates: an id is ours to issue, so one we never gave out answers `404`.\n\n**Required ability:** `contacts:write`","body":{"mode":"raw","raw":"{\n    \"name\": \"Mona Zaki\",\n    \"email\": \"mona@example.com\",\n    \"phone\": \"+201001234567\"\n}","options":{"raw":{"language":"json"}}}},"status":"OK","code":200,"header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n    \"data\": {\n        \"id\": 42,\n        \"name\": \"Mona Zaki\",\n        \"phone\": \"+201001234567\",\n        \"email\": \"mona@example.com\",\n        \"lifecycle_stage_id\": 3,\n        \"blocked\": false,\n        \"created_at\": \"2026-07-26T09:41:00+00:00\",\n        \"updated_at\": \"2026-07-26T09:41:00+00:00\"\n    }\n}","_postman_previewlanguage":"json"}]},{"name":"Delete a contact","request":{"method":"DELETE","header":[{"key":"Accept","value":"application/json"}],"url":{"raw":"{{base_url}}/contacts/{{contact_id}}","host":["{{base_url}}"],"path":["contacts","{{contact_id}}"]},"description":"Removes the contact from the workspace. Their conversations stay in the record. Deleting has its own ability, so a sync token can create and update without ever being able to empty the address book.\n\n**Required ability:** `contacts:delete`"},"response":[]},{"name":"Search contacts","request":{"method":"POST","header":[{"key":"Accept","value":"application/json"},{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{base_url}}/contacts/search","host":["{{base_url}}"],"path":["contacts","search"],"query":[{"key":"include","value":"","description":"Extra blocks to return with each contact, comma-separated: `labels`, `lifecycle`. Unknown names are ignored.","disabled":true},{"key":"cursor","value":"","description":"The `meta.next_cursor` from the previous page. Opaque — pass it back unchanged.","disabled":true}]},"description":"Everything the Contacts page can filter on, as a body — a `POST` because the question\ndoes not fit in a query string, not because it changes anything.\n\nRules are grouped: rules **inside** a group must all match, and a contact matching\n**any** group is returned. Sending no rules matches everyone, which is the unfiltered\nlist. The answer is a normal cursor page, so `include` and `cursor` work as they do on\n`GET /contacts`.\n\nThese are the same rules the segment builder writes and the same engine that decides a\ncampaign's audience, so a search here and a saved view in the panel cannot disagree\nabout who matches. Send `segment_id` to run a **saved segment** by id instead.\n\n## Rules\n\nA rule is `{\"attribute\": …, \"operator\": …, \"value\": …}`. These operators are\n``set`, `not_set`, `never`` and take no value; every other operator needs one.\n\n| Attribute | Operators |\n| --- | --- |\n| `lifecycle_stage` | `is` `is_not` |\n| `label` | `has` `has_not` |\n| `language` | `is` |\n| `channel` | `has` |\n| `last_seen` | `within_days` `older_than_days` `never` |\n| `created` | `within_days` `older_than_days` |\n| `name` | `contains` `set` `not_set` |\n| `email` | `contains` `set` `not_set` |\n| `phone` | `contains` `set` `not_set` |\n\nA rule the vocabulary does not recognise answers `422` naming the rule and what was\nallowed there — an unknown operator returning an empty page would be indistinguishable\nfrom nobody matching. Filtering by `phone` needs the view-phone permission, for the\nsame reason looking a contact up by phone does.\n\n**Required ability:** `contacts:read`","body":{"mode":"raw","raw":"{\n    \"search\": \"mona\",\n    \"match\": [\n        [\n            {\n                \"attribute\": \"label\",\n                \"operator\": \"has\",\n                \"value\": 5\n            },\n            {\n                \"attribute\": \"created\",\n                \"operator\": \"within_days\",\n                \"value\": 7\n            }\n        ]\n    ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","header":[{"key":"Accept","value":"application/json"},{"key":"Content-Type","value":"application/json"}],"url":{"raw":"{{base_url}}/contacts/search","host":["{{base_url}}"],"path":["contacts","search"],"query":[{"key":"include","value":"","description":"Extra blocks to return with each contact, comma-separated: `labels`, `lifecycle`. Unknown names are ignored.","disabled":true},{"key":"cursor","value":"","description":"The `meta.next_cursor` from the previous page. Opaque — pass it back unchanged.","disabled":true}]},"description":"Everything the Contacts page can filter on, as a body — a `POST` because the question\ndoes not fit in a query string, not because it changes anything.\n\nRules are grouped: rules **inside** a group must all match, and a contact matching\n**any** group is returned. Sending no rules matches everyone, which is the unfiltered\nlist. The answer is a normal cursor page, so `include` and `cursor` work as they do on\n`GET /contacts`.\n\nThese are the same rules the segment builder writes and the same engine that decides a\ncampaign's audience, so a search here and a saved view in the panel cannot disagree\nabout who matches. Send `segment_id` to run a **saved segment** by id instead.\n\n## Rules\n\nA rule is `{\"attribute\": …, \"operator\": …, \"value\": …}`. These operators are\n``set`, `not_set`, `never`` and take no value; every other operator needs one.\n\n| Attribute | Operators |\n| --- | --- |\n| `lifecycle_stage` | `is` `is_not` |\n| `label` | `has` `has_not` |\n| `language` | `is` |\n| `channel` | `has` |\n| `last_seen` | `within_days` `older_than_days` `never` |\n| `created` | `within_days` `older_than_days` |\n| `name` | `contains` `set` `not_set` |\n| `email` | `contains` `set` `not_set` |\n| `phone` | `contains` `set` `not_set` |\n\nA rule the vocabulary does not recognise answers `422` naming the rule and what was\nallowed there — an unknown operator returning an empty page would be indistinguishable\nfrom nobody matching. Filtering by `phone` needs the view-phone permission, for the\nsame reason looking a contact up by phone does.\n\n**Required ability:** `contacts:read`","body":{"mode":"raw","raw":"{\n    \"search\": \"mona\",\n    \"match\": [\n        [\n            {\n                \"attribute\": \"label\",\n                \"operator\": \"has\",\n                \"value\": 5\n            },\n            {\n                \"attribute\": \"created\",\n                \"operator\": \"within_days\",\n                \"value\": 7\n            }\n        ]\n    ]\n}","options":{"raw":{"language":"json"}}}},"status":"OK","code":200,"header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n    \"data\": [\n        {\n            \"id\": 42,\n            \"name\": \"Mona Zaki\",\n            \"phone\": \"+201001234567\",\n            \"email\": \"mona@example.com\",\n            \"lifecycle_stage_id\": 3,\n            \"blocked\": false,\n            \"created_at\": \"2026-07-26T09:41:00+00:00\",\n            \"updated_at\": \"2026-07-26T09:41:00+00:00\"\n        }\n    ],\n    \"meta\": {\n        \"next_cursor\": null,\n        \"per_page\": 50\n    }\n}","_postman_previewlanguage":"json"}]},{"name":"Merge two contacts","request":{"method":"POST","header":[{"key":"Accept","value":"application/json"},{"key":"Content-Type","value":"application/json"},{"key":"Idempotency-Key","value":"{{$guid}}","description":"Any unique string of your own. Retrying with the same key returns the first response instead of repeating the work. Remembered for 24 hours."}],"url":{"raw":"{{base_url}}/contacts/merge","host":["{{base_url}}"],"path":["contacts","merge"]},"description":"Folds one contact into another — usually the same person who reached in on two channels. The loser's conversations, deals, ratings, channel handles, labels and custom fields all move across; the survivor's own values win a clash and fill any gaps from the loser; the loser is then removed. Answers with the survivor.\n\n**Which one survives is yours to say.** Both are named and there is no default: an API that guessed — newest wins, most conversations wins — would quietly destroy the wrong record at scale.\n\nGated on the delete ability, because a contact goes away.\n\n**Required ability:** `contacts:delete`","body":{"mode":"raw","raw":"{\n    \"survivor\": \"id:42\",\n    \"loser\": \"phone:+201001234567\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","header":[{"key":"Accept","value":"application/json"},{"key":"Content-Type","value":"application/json"},{"key":"Idempotency-Key","value":"{{$guid}}","description":"Any unique string of your own. Retrying with the same key returns the first response instead of repeating the work. Remembered for 24 hours."}],"url":{"raw":"{{base_url}}/contacts/merge","host":["{{base_url}}"],"path":["contacts","merge"]},"description":"Folds one contact into another — usually the same person who reached in on two channels. The loser's conversations, deals, ratings, channel handles, labels and custom fields all move across; the survivor's own values win a clash and fill any gaps from the loser; the loser is then removed. Answers with the survivor.\n\n**Which one survives is yours to say.** Both are named and there is no default: an API that guessed — newest wins, most conversations wins — would quietly destroy the wrong record at scale.\n\nGated on the delete ability, because a contact goes away.\n\n**Required ability:** `contacts:delete`","body":{"mode":"raw","raw":"{\n    \"survivor\": \"id:42\",\n    \"loser\": \"phone:+201001234567\"\n}","options":{"raw":{"language":"json"}}}},"status":"OK","code":200,"header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n    \"data\": {\n        \"id\": 42,\n        \"name\": \"Mona Zaki\",\n        \"phone\": \"+201001234567\",\n        \"email\": \"mona@example.com\",\n        \"lifecycle_stage_id\": 3,\n        \"blocked\": false,\n        \"created_at\": \"2026-07-26T09:41:00+00:00\",\n        \"updated_at\": \"2026-07-26T09:41:00+00:00\"\n    }\n}","_postman_previewlanguage":"json"}]},{"name":"Move a contact's lifecycle stage","request":{"method":"PUT","header":[{"key":"Accept","value":"application/json"},{"key":"Content-Type","value":"application/json"},{"key":"Idempotency-Key","value":"{{$guid}}","description":"Any unique string of your own. Retrying with the same key returns the first response instead of repeating the work. Remembered for 24 hours."}],"url":{"raw":"{{base_url}}/contacts/{{contact_id}}/lifecycle","host":["{{base_url}}"],"path":["contacts","{{contact_id}}","lifecycle"]},"description":"Moves the contact along the lifecycle, by stage id or exact name. The lifecycle **is** the pipeline here, so this is not a field being set: reaching the winning stage settles the contact's open deals, and the move is written to their timeline.\n\nAnswers with the stage they were on before — `null` when they were already on the one you asked for, so \"nothing moved\" is stated rather than left to be inferred.\n\nThere is no way to remove a lifecycle: a contact always sits somewhere on the journey, starting at the default stage the moment they are created.\n\n**Required ability:** `contacts:write`","body":{"mode":"raw","raw":"{\n    \"stage\": \"Customer\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PUT","header":[{"key":"Accept","value":"application/json"},{"key":"Content-Type","value":"application/json"},{"key":"Idempotency-Key","value":"{{$guid}}","description":"Any unique string of your own. Retrying with the same key returns the first response instead of repeating the work. Remembered for 24 hours."}],"url":{"raw":"{{base_url}}/contacts/{{contact_id}}/lifecycle","host":["{{base_url}}"],"path":["contacts","{{contact_id}}","lifecycle"]},"description":"Moves the contact along the lifecycle, by stage id or exact name. The lifecycle **is** the pipeline here, so this is not a field being set: reaching the winning stage settles the contact's open deals, and the move is written to their timeline.\n\nAnswers with the stage they were on before — `null` when they were already on the one you asked for, so \"nothing moved\" is stated rather than left to be inferred.\n\nThere is no way to remove a lifecycle: a contact always sits somewhere on the journey, starting at the default stage the moment they are created.\n\n**Required ability:** `contacts:write`","body":{"mode":"raw","raw":"{\n    \"stage\": \"Customer\"\n}","options":{"raw":{"language":"json"}}}},"status":"OK","code":200,"header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n    \"data\": {\n        \"contact_id\": 42,\n        \"previous\": {\n            \"id\": 1,\n            \"name\": \"New\",\n            \"role\": \"default\",\n            \"outcome\": null\n        },\n        \"lifecycle\": {\n            \"id\": 4,\n            \"name\": \"Customer\",\n            \"role\": null,\n            \"outcome\": \"won\"\n        }\n    }\n}","_postman_previewlanguage":"json"}]},{"name":"List a contact's labels","request":{"method":"GET","header":[{"key":"Accept","value":"application/json"}],"url":{"raw":"{{base_url}}/contacts/{{contact_id}}/labels","host":["{{base_url}}"],"path":["contacts","{{contact_id}}","labels"]},"description":"The labels on one contact, by name — tags, in the vocabulary some other tools use.\n\n**Required ability:** `contacts:read`"},"response":[{"name":"OK","originalRequest":{"method":"GET","header":[{"key":"Accept","value":"application/json"}],"url":{"raw":"{{base_url}}/contacts/{{contact_id}}/labels","host":["{{base_url}}"],"path":["contacts","{{contact_id}}","labels"]},"description":"The labels on one contact, by name — tags, in the vocabulary some other tools use.\n\n**Required ability:** `contacts:read`"},"status":"OK","code":200,"header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n    \"data\": [\n        {\n            \"id\": 5,\n            \"name\": \"VIP\"\n        }\n    ]\n}","_postman_previewlanguage":"json"}]},{"name":"Add labels to a contact","request":{"method":"POST","header":[{"key":"Accept","value":"application/json"},{"key":"Content-Type","value":"application/json"},{"key":"Idempotency-Key","value":"{{$guid}}","description":"Any unique string of your own. Retrying with the same key returns the first response instead of repeating the work. Remembered for 24 hours."}],"url":{"raw":"{{base_url}}/contacts/{{contact_id}}/labels","host":["{{base_url}}"],"path":["contacts","{{contact_id}}","labels"]},"description":"Attaches labels the workspace already has, by id or by exact name, and answers with the contact's labels afterwards. Adding one that is already there changes nothing.\n\n**Labels are never invented.** A name the workspace does not have fails the whole call with `422` rather than creating it — a typo in an integration would otherwise grow the label list until nobody can find anything. Create labels in Settings.\n\n**Required ability:** `contacts:write`","body":{"mode":"raw","raw":"{\n    \"labels\": [\n        \"VIP\",\n        12\n    ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"POST","header":[{"key":"Accept","value":"application/json"},{"key":"Content-Type","value":"application/json"},{"key":"Idempotency-Key","value":"{{$guid}}","description":"Any unique string of your own. Retrying with the same key returns the first response instead of repeating the work. Remembered for 24 hours."}],"url":{"raw":"{{base_url}}/contacts/{{contact_id}}/labels","host":["{{base_url}}"],"path":["contacts","{{contact_id}}","labels"]},"description":"Attaches labels the workspace already has, by id or by exact name, and answers with the contact's labels afterwards. Adding one that is already there changes nothing.\n\n**Labels are never invented.** A name the workspace does not have fails the whole call with `422` rather than creating it — a typo in an integration would otherwise grow the label list until nobody can find anything. Create labels in Settings.\n\n**Required ability:** `contacts:write`","body":{"mode":"raw","raw":"{\n    \"labels\": [\n        \"VIP\",\n        12\n    ]\n}","options":{"raw":{"language":"json"}}}},"status":"OK","code":200,"header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n    \"data\": [\n        {\n            \"id\": 5,\n            \"name\": \"VIP\"\n        }\n    ]\n}","_postman_previewlanguage":"json"}]},{"name":"Remove a label from a contact","request":{"method":"DELETE","header":[{"key":"Accept","value":"application/json"}],"url":{"raw":"{{base_url}}/contacts/{{contact_id}}/labels/{{label}}","host":["{{base_url}}"],"path":["contacts","{{contact_id}}","labels","{{label}}"]},"description":"Takes one label off a contact and answers with the labels that remain. The label itself is untouched — it stays in the workspace for everyone else. Removing one the contact does not carry is not an error.\n\n**Required ability:** `contacts:write`"},"response":[{"name":"OK","originalRequest":{"method":"DELETE","header":[{"key":"Accept","value":"application/json"}],"url":{"raw":"{{base_url}}/contacts/{{contact_id}}/labels/{{label}}","host":["{{base_url}}"],"path":["contacts","{{contact_id}}","labels","{{label}}"]},"description":"Takes one label off a contact and answers with the labels that remain. The label itself is untouched — it stays in the workspace for everyone else. Removing one the contact does not carry is not an error.\n\n**Required ability:** `contacts:write`"},"status":"OK","code":200,"header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n    \"data\": []\n}","_postman_previewlanguage":"json"}]}]},{"name":"Conversations","description":"Threads with a contact on a channel.","item":[{"name":"List conversations","request":{"method":"GET","header":[{"key":"Accept","value":"application/json"}],"url":{"raw":"{{base_url}}/conversations","host":["{{base_url}}"],"path":["conversations"],"query":[{"key":"status","value":"","description":"Only threads in this status.","disabled":true},{"key":"contact_id","value":"","description":"","disabled":true},{"key":"channel_id","value":"","description":"","disabled":true},{"key":"per_page","value":"","description":"Rows per page. Defaults to 50, capped at 100.","disabled":true},{"key":"cursor","value":"","description":"The `meta.next_cursor` from the previous page. Opaque — pass it back unchanged.","disabled":true}]},"description":"Threads in the workspace, newest first, each with the channel it runs on.\n\n**Required ability:** `conversations:read`"},"response":[{"name":"OK","originalRequest":{"method":"GET","header":[{"key":"Accept","value":"application/json"}],"url":{"raw":"{{base_url}}/conversations","host":["{{base_url}}"],"path":["conversations"],"query":[{"key":"status","value":"","description":"Only threads in this status.","disabled":true},{"key":"contact_id","value":"","description":"","disabled":true},{"key":"channel_id","value":"","description":"","disabled":true},{"key":"per_page","value":"","description":"Rows per page. Defaults to 50, capped at 100.","disabled":true},{"key":"cursor","value":"","description":"The `meta.next_cursor` from the previous page. Opaque — pass it back unchanged.","disabled":true}]},"description":"Threads in the workspace, newest first, each with the channel it runs on.\n\n**Required ability:** `conversations:read`"},"status":"OK","code":200,"header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n    \"data\": [\n        {\n            \"id\": 1024,\n            \"contact_id\": 42,\n            \"channel\": {\n                \"id\": 2,\n                \"name\": \"Clinic WhatsApp\",\n                \"type\": \"whatsapp\"\n            },\n            \"status\": \"open\",\n            \"subject\": null,\n            \"assignee\": {\n                \"type\": \"user\",\n                \"id\": 7\n            },\n            \"last_message_at\": \"2026-07-26T09:41:00+00:00\",\n            \"closed_at\": null,\n            \"created_at\": \"2026-07-26T09:12:00+00:00\"\n        }\n    ],\n    \"meta\": {\n        \"next_cursor\": null,\n        \"per_page\": 50\n    }\n}","_postman_previewlanguage":"json"}]},{"name":"Open a conversation","request":{"method":"POST","header":[{"key":"Accept","value":"application/json"},{"key":"Content-Type","value":"application/json"},{"key":"Idempotency-Key","value":"{{$guid}}","description":"Any unique string of your own. Retrying with the same key returns the first response instead of repeating the work. Remembered for 24 hours."}],"url":{"raw":"{{base_url}}/conversations","host":["{{base_url}}"],"path":["conversations"]},"description":"Starts a thread with a contact on one of your channels.\n\nA contact has at most one live thread per channel, so an open one answers `200` with the thread you already have rather than opening a second — a customer with two conversations on the same number is a mess for the agent, not a feature. A genuinely new thread answers `201`.\n\n**Required ability:** `conversations:write`","body":{"mode":"raw","raw":"{\n    \"contact\": \"phone:+201001234567\",\n    \"channel_id\": \"\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"Created","originalRequest":{"method":"POST","header":[{"key":"Accept","value":"application/json"},{"key":"Content-Type","value":"application/json"},{"key":"Idempotency-Key","value":"{{$guid}}","description":"Any unique string of your own. Retrying with the same key returns the first response instead of repeating the work. Remembered for 24 hours."}],"url":{"raw":"{{base_url}}/conversations","host":["{{base_url}}"],"path":["conversations"]},"description":"Starts a thread with a contact on one of your channels.\n\nA contact has at most one live thread per channel, so an open one answers `200` with the thread you already have rather than opening a second — a customer with two conversations on the same number is a mess for the agent, not a feature. A genuinely new thread answers `201`.\n\n**Required ability:** `conversations:write`","body":{"mode":"raw","raw":"{\n    \"contact\": \"phone:+201001234567\",\n    \"channel_id\": \"\"\n}","options":{"raw":{"language":"json"}}}},"status":"Created","code":201,"header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n    \"data\": {\n        \"id\": 1024,\n        \"contact_id\": 42,\n        \"channel\": {\n            \"id\": 2,\n            \"name\": \"Clinic WhatsApp\",\n            \"type\": \"whatsapp\"\n        },\n        \"status\": \"open\",\n        \"subject\": null,\n        \"assignee\": {\n            \"type\": \"user\",\n            \"id\": 7\n        },\n        \"last_message_at\": \"2026-07-26T09:41:00+00:00\",\n        \"closed_at\": null,\n        \"created_at\": \"2026-07-26T09:12:00+00:00\"\n    }\n}","_postman_previewlanguage":"json"}]},{"name":"Get a conversation","request":{"method":"GET","header":[{"key":"Accept","value":"application/json"}],"url":{"raw":"{{base_url}}/conversations/{{conversation_id}}","host":["{{base_url}}"],"path":["conversations","{{conversation_id}}"]},"description":"One thread with its channel and current assignee.\n\n**Required ability:** `conversations:read`"},"response":[{"name":"OK","originalRequest":{"method":"GET","header":[{"key":"Accept","value":"application/json"}],"url":{"raw":"{{base_url}}/conversations/{{conversation_id}}","host":["{{base_url}}"],"path":["conversations","{{conversation_id}}"]},"description":"One thread with its channel and current assignee.\n\n**Required ability:** `conversations:read`"},"status":"OK","code":200,"header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n    \"data\": {\n        \"id\": 1024,\n        \"contact_id\": 42,\n        \"channel\": {\n            \"id\": 2,\n            \"name\": \"Clinic WhatsApp\",\n            \"type\": \"whatsapp\"\n        },\n        \"status\": \"open\",\n        \"subject\": null,\n        \"assignee\": {\n            \"type\": \"user\",\n            \"id\": 7\n        },\n        \"last_message_at\": \"2026-07-26T09:41:00+00:00\",\n        \"closed_at\": null,\n        \"created_at\": \"2026-07-26T09:12:00+00:00\"\n    }\n}","_postman_previewlanguage":"json"}]},{"name":"Change a conversation","request":{"method":"PATCH","header":[{"key":"Accept","value":"application/json"},{"key":"Content-Type","value":"application/json"},{"key":"Idempotency-Key","value":"{{$guid}}","description":"Any unique string of your own. Retrying with the same key returns the first response instead of repeating the work. Remembered for 24 hours."}],"url":{"raw":"{{base_url}}/conversations/{{conversation_id}}","host":["{{base_url}}"],"path":["conversations","{{conversation_id}}"]},"description":"Status, owner, team and priority. Only the keys you send are acted on.\n\nClosing does everything closing does in the panel — cancels the thread's flow enrollments, drops a pending AI follow-up, fires the resolved trigger, may send a CSAT survey and summarises the thread. This is why there is no \"just set the status\" shortcut.\n\n**The assignee may be an AI agent.** `{\"type\": \"ai\", \"id\": 3}` hands the thread to one of your agents, which then answers as a teammate would — and if the customer is already waiting, it replies at once rather than waiting for the next inbound message. `null` unassigns whoever owns it. A team is separate from an assignee: `team_id` routes the thread to a queue, and both can be set at once.\n\n**Required ability:** `conversations:write`","body":{"mode":"raw","raw":"{\n    \"status\": \"closed\",\n    \"category\": \"Resolved\",\n    \"summary\": \"Refunded order 4821 and confirmed by email.\",\n    \"snoozed_until\": \"2026-08-05T09:00:00+00:00\",\n    \"priority\": \"high\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PATCH","header":[{"key":"Accept","value":"application/json"},{"key":"Content-Type","value":"application/json"},{"key":"Idempotency-Key","value":"{{$guid}}","description":"Any unique string of your own. Retrying with the same key returns the first response instead of repeating the work. Remembered for 24 hours."}],"url":{"raw":"{{base_url}}/conversations/{{conversation_id}}","host":["{{base_url}}"],"path":["conversations","{{conversation_id}}"]},"description":"Status, owner, team and priority. Only the keys you send are acted on.\n\nClosing does everything closing does in the panel — cancels the thread's flow enrollments, drops a pending AI follow-up, fires the resolved trigger, may send a CSAT survey and summarises the thread. This is why there is no \"just set the status\" shortcut.\n\n**The assignee may be an AI agent.** `{\"type\": \"ai\", \"id\": 3}` hands the thread to one of your agents, which then answers as a teammate would — and if the customer is already waiting, it replies at once rather than waiting for the next inbound message. `null` unassigns whoever owns it. A team is separate from an assignee: `team_id` routes the thread to a queue, and both can be set at once.\n\n**Required ability:** `conversations:write`","body":{"mode":"raw","raw":"{\n    \"status\": \"closed\",\n    \"category\": \"Resolved\",\n    \"summary\": \"Refunded order 4821 and confirmed by email.\",\n    \"snoozed_until\": \"2026-08-05T09:00:00+00:00\",\n    \"priority\": \"high\"\n}","options":{"raw":{"language":"json"}}}},"status":"OK","code":200,"header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n    \"data\": {\n        \"id\": 1024,\n        \"contact_id\": 42,\n        \"channel\": {\n            \"id\": 2,\n            \"name\": \"Clinic WhatsApp\",\n            \"type\": \"whatsapp\"\n        },\n        \"status\": \"open\",\n        \"subject\": null,\n        \"assignee\": {\n            \"type\": \"user\",\n            \"id\": 7\n        },\n        \"last_message_at\": \"2026-07-26T09:41:00+00:00\",\n        \"closed_at\": null,\n        \"created_at\": \"2026-07-26T09:12:00+00:00\"\n    }\n}","_postman_previewlanguage":"json"}]},{"name":"List closing notes","request":{"method":"GET","header":[{"key":"Accept","value":"application/json"}],"url":{"raw":"{{base_url}}/closing-notes","host":["{{base_url}}"],"path":["closing-notes"]},"description":"The reasons this workspace closes conversations with, in the order it arranged them. Read this before closing a thread with a `category`.\n\nAn empty list is a normal state — a workspace that has not configured any simply closes threads without a reason.\n\n**Required ability:** `conversations:read`"},"response":[{"name":"OK","originalRequest":{"method":"GET","header":[{"key":"Accept","value":"application/json"}],"url":{"raw":"{{base_url}}/closing-notes","host":["{{base_url}}"],"path":["closing-notes"]},"description":"The reasons this workspace closes conversations with, in the order it arranged them. Read this before closing a thread with a `category`.\n\nAn empty list is a normal state — a workspace that has not configured any simply closes threads without a reason.\n\n**Required ability:** `conversations:read`"},"status":"OK","code":200,"header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n    \"data\": [\n        {\n            \"category\": \"Resolved\",\n            \"description\": \"The customer got what they needed\"\n        }\n    ]\n}","_postman_previewlanguage":"json"}]}]},{"name":"Messages","description":"Reading a thread, and sending into it.","item":[{"name":"List messages","request":{"method":"GET","header":[{"key":"Accept","value":"application/json"}],"url":{"raw":"{{base_url}}/conversations/{{conversation_id}}/messages","host":["{{base_url}}"],"path":["conversations","{{conversation_id}}","messages"],"query":[{"key":"per_page","value":"","description":"Rows per page. Defaults to 50, capped at 100.","disabled":true},{"key":"cursor","value":"","description":"The `meta.next_cursor` from the previous page. Opaque — pass it back unchanged.","disabled":true}]},"description":"A thread's messages, newest first.\n\n**Required ability:** `conversations:read`"},"response":[{"name":"OK","originalRequest":{"method":"GET","header":[{"key":"Accept","value":"application/json"}],"url":{"raw":"{{base_url}}/conversations/{{conversation_id}}/messages","host":["{{base_url}}"],"path":["conversations","{{conversation_id}}","messages"],"query":[{"key":"per_page","value":"","description":"Rows per page. Defaults to 50, capped at 100.","disabled":true},{"key":"cursor","value":"","description":"The `meta.next_cursor` from the previous page. Opaque — pass it back unchanged.","disabled":true}]},"description":"A thread's messages, newest first.\n\n**Required ability:** `conversations:read`"},"status":"OK","code":200,"header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n    \"data\": [\n        {\n            \"id\": 9001,\n            \"conversation_id\": 1024,\n            \"type\": \"message\",\n            \"direction\": \"inbound\",\n            \"sender_type\": \"contact\",\n            \"user_id\": null,\n            \"body\": \"Do you open on Friday?\",\n            \"content_type\": \"text\",\n            \"status\": null,\n            \"created_at\": \"2026-07-26T09:41:00+00:00\"\n        }\n    ],\n    \"meta\": {\n        \"next_cursor\": null,\n        \"per_page\": 50\n    }\n}","_postman_previewlanguage":"json"}]},{"name":"Send a message","request":{"method":"POST","header":[{"key":"Accept","value":"application/json"},{"key":"Content-Type","value":"application/json"},{"key":"Idempotency-Key","value":"{{$guid}}","description":"Any unique string of your own. Retrying with the same key returns the first response instead of repeating the work. Remembered for 24 hours."}],"url":{"raw":"{{base_url}}/conversations/{{conversation_id}}/messages","host":["{{base_url}}"],"path":["conversations","{{conversation_id}}","messages"]},"description":"Sends an outbound message into the thread, on whichever channel it runs on,\nattributed to the token's teammate. This is a real send: it takes the same\npath as the inbox composer, so delivery, automation and reporting all behave\nidentically.\n\n## Kinds\n\n`type: \"text\"` — a plain message, up to 4096 characters.\n\n`type: \"attachment\"` — a file, given as a **public URL we fetch** rather than\nan upload, since you already have the file and multipart bodies are awkward\nfrom most automation platforms. Up to 25 MB; a URL we cannot reach, or one\npointing inside a private network, is refused.\n\n`type: \"template\"` — an **approved WhatsApp template** by name, with its\n`parameters` in order. Only approved templates on the thread's own channel\ncan be sent. If the template exists in more than one language you must say\nwhich, since two languages of one template are two different messages.\n\n`type: \"buttons\"` — the `text` as a prompt with up to ten tappable quick\nreplies. A channel that renders buttons natively (Telegram, the web widget)\nshows them as buttons; every other channel shows the text. Tapping one sends\nits **exact label back as a normal inbound message** — which is also why two\nbuttons may not carry the same text. This is the same shape the workflow\nengine's Buttons step sends.\n\n## The 24-hour window\n\nWhatsApp applies Meta's rules: outside the 24-hour customer service window a\nfree-form message is refused by Meta and arrives back as a failed message. A\n**template is the only thing Meta accepts there**, which is why this endpoint\ncan send one.\n\nSending `{\"body\": \"…\"}` with no `type` still works and means a text message.\n\n**Required ability:** `messages:send`","body":{"mode":"raw","raw":"{\n    \"type\": \"text\",\n    \"text\": \"Your order is on its way!\",\n    \"reply_to\": \"PAGE-1_777\",\n    \"url\": \"https://files.example.com/invoice-4821.pdf\",\n    \"buttons\": [\n        \"Sales\",\n        \"Support\"\n    ],\n    \"template\": \"order_shipped\",\n    \"language\": \"ar\",\n    \"parameters\": [\n        \"4821\",\n        \"tomorrow\"\n    ]\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"Created","originalRequest":{"method":"POST","header":[{"key":"Accept","value":"application/json"},{"key":"Content-Type","value":"application/json"},{"key":"Idempotency-Key","value":"{{$guid}}","description":"Any unique string of your own. Retrying with the same key returns the first response instead of repeating the work. Remembered for 24 hours."}],"url":{"raw":"{{base_url}}/conversations/{{conversation_id}}/messages","host":["{{base_url}}"],"path":["conversations","{{conversation_id}}","messages"]},"description":"Sends an outbound message into the thread, on whichever channel it runs on,\nattributed to the token's teammate. This is a real send: it takes the same\npath as the inbox composer, so delivery, automation and reporting all behave\nidentically.\n\n## Kinds\n\n`type: \"text\"` — a plain message, up to 4096 characters.\n\n`type: \"attachment\"` — a file, given as a **public URL we fetch** rather than\nan upload, since you already have the file and multipart bodies are awkward\nfrom most automation platforms. Up to 25 MB; a URL we cannot reach, or one\npointing inside a private network, is refused.\n\n`type: \"template\"` — an **approved WhatsApp template** by name, with its\n`parameters` in order. Only approved templates on the thread's own channel\ncan be sent. If the template exists in more than one language you must say\nwhich, since two languages of one template are two different messages.\n\n`type: \"buttons\"` — the `text` as a prompt with up to ten tappable quick\nreplies. A channel that renders buttons natively (Telegram, the web widget)\nshows them as buttons; every other channel shows the text. Tapping one sends\nits **exact label back as a normal inbound message** — which is also why two\nbuttons may not carry the same text. This is the same shape the workflow\nengine's Buttons step sends.\n\n## The 24-hour window\n\nWhatsApp applies Meta's rules: outside the 24-hour customer service window a\nfree-form message is refused by Meta and arrives back as a failed message. A\n**template is the only thing Meta accepts there**, which is why this endpoint\ncan send one.\n\nSending `{\"body\": \"…\"}` with no `type` still works and means a text message.\n\n**Required ability:** `messages:send`","body":{"mode":"raw","raw":"{\n    \"type\": \"text\",\n    \"text\": \"Your order is on its way!\",\n    \"reply_to\": \"PAGE-1_777\",\n    \"url\": \"https://files.example.com/invoice-4821.pdf\",\n    \"buttons\": [\n        \"Sales\",\n        \"Support\"\n    ],\n    \"template\": \"order_shipped\",\n    \"language\": \"ar\",\n    \"parameters\": [\n        \"4821\",\n        \"tomorrow\"\n    ]\n}","options":{"raw":{"language":"json"}}}},"status":"Created","code":201,"header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n    \"data\": {\n        \"id\": 9001,\n        \"conversation_id\": 1024,\n        \"type\": \"message\",\n        \"direction\": \"outbound\",\n        \"sender_type\": \"contact\",\n        \"user_id\": null,\n        \"body\": \"Your order is on its way!\",\n        \"content_type\": \"text\",\n        \"status\": null,\n        \"created_at\": \"2026-07-26T09:41:00+00:00\"\n    }\n}","_postman_previewlanguage":"json"}]},{"name":"Add an internal note","request":{"method":"POST","header":[{"key":"Accept","value":"application/json"},{"key":"Content-Type","value":"application/json"},{"key":"Idempotency-Key","value":"{{$guid}}","description":"Any unique string of your own. Retrying with the same key returns the first response instead of repeating the work. Remembered for 24 hours."}],"url":{"raw":"{{base_url}}/conversations/{{conversation_id}}/notes","host":["{{base_url}}"],"path":["conversations","{{conversation_id}}","notes"]},"description":"A note the team can see and the contact never does — what an integration writes when it has something to report (\"refund processed\", \"order shipped\") without messaging the customer.\n\nMentioned teammates are notified, exactly as an @mention typed in the inbox. Every id must be an active teammate here: silently dropping one would leave you believing somebody was told who never was.\n\nAnswers to the conversation ability rather than `messages:send`, because nothing is sent to the customer.\n\n**Required ability:** `conversations:write`","body":{"mode":"raw","raw":"{\n    \"body\": \"Refunded order 4821.\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"Created","originalRequest":{"method":"POST","header":[{"key":"Accept","value":"application/json"},{"key":"Content-Type","value":"application/json"},{"key":"Idempotency-Key","value":"{{$guid}}","description":"Any unique string of your own. Retrying with the same key returns the first response instead of repeating the work. Remembered for 24 hours."}],"url":{"raw":"{{base_url}}/conversations/{{conversation_id}}/notes","host":["{{base_url}}"],"path":["conversations","{{conversation_id}}","notes"]},"description":"A note the team can see and the contact never does — what an integration writes when it has something to report (\"refund processed\", \"order shipped\") without messaging the customer.\n\nMentioned teammates are notified, exactly as an @mention typed in the inbox. Every id must be an active teammate here: silently dropping one would leave you believing somebody was told who never was.\n\nAnswers to the conversation ability rather than `messages:send`, because nothing is sent to the customer.\n\n**Required ability:** `conversations:write`","body":{"mode":"raw","raw":"{\n    \"body\": \"Refunded order 4821.\"\n}","options":{"raw":{"language":"json"}}}},"status":"Created","code":201,"header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n    \"data\": {\n        \"id\": 9001,\n        \"conversation_id\": 1024,\n        \"type\": \"note\",\n        \"direction\": null,\n        \"sender_type\": \"contact\",\n        \"user_id\": null,\n        \"body\": \"Refunded order 4821.\",\n        \"content_type\": \"text\",\n        \"status\": null,\n        \"created_at\": \"2026-07-26T09:41:00+00:00\"\n    }\n}","_postman_previewlanguage":"json"}]},{"name":"Get a message","request":{"method":"GET","header":[{"key":"Accept","value":"application/json"}],"url":{"raw":"{{base_url}}/messages/{{message_id}}","host":["{{base_url}}"],"path":["messages","{{message_id}}"]},"description":"One message by id — how you resolve the `message.id` a webhook handed you without paging the whole thread.\n\n**Required ability:** `conversations:read`"},"response":[{"name":"OK","originalRequest":{"method":"GET","header":[{"key":"Accept","value":"application/json"}],"url":{"raw":"{{base_url}}/messages/{{message_id}}","host":["{{base_url}}"],"path":["messages","{{message_id}}"]},"description":"One message by id — how you resolve the `message.id` a webhook handed you without paging the whole thread.\n\n**Required ability:** `conversations:read`"},"status":"OK","code":200,"header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n    \"data\": {\n        \"id\": 9001,\n        \"conversation_id\": 1024,\n        \"type\": \"message\",\n        \"direction\": \"inbound\",\n        \"sender_type\": \"contact\",\n        \"user_id\": null,\n        \"body\": \"Do you open on Friday?\",\n        \"content_type\": \"text\",\n        \"status\": null,\n        \"created_at\": \"2026-07-26T09:41:00+00:00\"\n    }\n}","_postman_previewlanguage":"json"}]}]},{"name":"Deals","description":"Revenue opportunities moving along their pipelines.","item":[{"name":"List deals","request":{"method":"GET","header":[{"key":"Accept","value":"application/json"}],"url":{"raw":"{{base_url}}/deals","host":["{{base_url}}"],"path":["deals"],"query":[{"key":"status","value":"","description":"`open` for deals still in play, or a settled status.","disabled":true},{"key":"contact_id","value":"","description":"","disabled":true},{"key":"pipeline_id","value":"","description":"","disabled":true},{"key":"per_page","value":"","description":"Rows per page. Defaults to 50, capped at 100.","disabled":true},{"key":"cursor","value":"","description":"The `meta.next_cursor` from the previous page. Opaque — pass it back unchanged.","disabled":true}]},"description":"Deals on their pipelines, newest first.\n\n**Required ability:** `deals:read`"},"response":[{"name":"OK","originalRequest":{"method":"GET","header":[{"key":"Accept","value":"application/json"}],"url":{"raw":"{{base_url}}/deals","host":["{{base_url}}"],"path":["deals"],"query":[{"key":"status","value":"","description":"`open` for deals still in play, or a settled status.","disabled":true},{"key":"contact_id","value":"","description":"","disabled":true},{"key":"pipeline_id","value":"","description":"","disabled":true},{"key":"per_page","value":"","description":"Rows per page. Defaults to 50, capped at 100.","disabled":true},{"key":"cursor","value":"","description":"The `meta.next_cursor` from the previous page. Opaque — pass it back unchanged.","disabled":true}]},"description":"Deals on their pipelines, newest first.\n\n**Required ability:** `deals:read`"},"status":"OK","code":200,"header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n    \"data\": [\n        {\n            \"id\": 88,\n            \"name\": \"Clinic fit-out\",\n            \"pipeline\": {\n                \"id\": 1,\n                \"name\": \"Sales pipeline\"\n            },\n            \"stage\": {\n                \"id\": 3,\n                \"name\": \"Qualified\"\n            },\n            \"status\": \"open\",\n            \"amount\": \"25000.00\",\n            \"close_date\": null,\n            \"owner_id\": 7,\n            \"organization_id\": null,\n            \"source\": \"website\",\n            \"closed_at\": null,\n            \"closed_lost_reason\": null,\n            \"contact_ids\": [\n                42\n            ],\n            \"created_at\": \"2026-07-26T09:41:00+00:00\"\n        }\n    ],\n    \"meta\": {\n        \"next_cursor\": null,\n        \"per_page\": 50\n    }\n}","_postman_previewlanguage":"json"}]},{"name":"Create a deal","request":{"method":"POST","header":[{"key":"Accept","value":"application/json"},{"key":"Content-Type","value":"application/json"},{"key":"Idempotency-Key","value":"{{$guid}}","description":"Any unique string of your own. Retrying with the same key returns the first response instead of repeating the work. Remembered for 24 hours."}],"url":{"raw":"{{base_url}}/deals","host":["{{base_url}}"],"path":["deals"]},"description":"Opens a deal, landing on the given stage or the default pipeline's first stage. A `contact_id` must belong to this workspace, or the call answers `422`.\n\n**Required ability:** `deals:write`","body":{"mode":"raw","raw":"{\n    \"name\": \"Clinic fit-out\",\n    \"contact_id\": 42,\n    \"amount\": 25000,\n    \"source\": \"website\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"Created","originalRequest":{"method":"POST","header":[{"key":"Accept","value":"application/json"},{"key":"Content-Type","value":"application/json"},{"key":"Idempotency-Key","value":"{{$guid}}","description":"Any unique string of your own. Retrying with the same key returns the first response instead of repeating the work. Remembered for 24 hours."}],"url":{"raw":"{{base_url}}/deals","host":["{{base_url}}"],"path":["deals"]},"description":"Opens a deal, landing on the given stage or the default pipeline's first stage. A `contact_id` must belong to this workspace, or the call answers `422`.\n\n**Required ability:** `deals:write`","body":{"mode":"raw","raw":"{\n    \"name\": \"Clinic fit-out\",\n    \"contact_id\": 42,\n    \"amount\": 25000,\n    \"source\": \"website\"\n}","options":{"raw":{"language":"json"}}}},"status":"Created","code":201,"header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n    \"data\": {\n        \"id\": 88,\n        \"name\": \"Clinic fit-out\",\n        \"pipeline\": {\n            \"id\": 1,\n            \"name\": \"Sales pipeline\"\n        },\n        \"stage\": {\n            \"id\": 3,\n            \"name\": \"Qualified\"\n        },\n        \"status\": \"open\",\n        \"amount\": \"25000.00\",\n        \"close_date\": null,\n        \"owner_id\": 7,\n        \"organization_id\": null,\n        \"source\": \"website\",\n        \"closed_at\": null,\n        \"closed_lost_reason\": null,\n        \"contact_ids\": [\n            42\n        ],\n        \"created_at\": \"2026-07-26T09:41:00+00:00\"\n    }\n}","_postman_previewlanguage":"json"}]},{"name":"Get a deal","request":{"method":"GET","header":[{"key":"Accept","value":"application/json"}],"url":{"raw":"{{base_url}}/deals/{{deal_id}}","host":["{{base_url}}"],"path":["deals","{{deal_id}}"]},"description":"One deal by id.\n\n**Required ability:** `deals:read`"},"response":[{"name":"OK","originalRequest":{"method":"GET","header":[{"key":"Accept","value":"application/json"}],"url":{"raw":"{{base_url}}/deals/{{deal_id}}","host":["{{base_url}}"],"path":["deals","{{deal_id}}"]},"description":"One deal by id.\n\n**Required ability:** `deals:read`"},"status":"OK","code":200,"header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n    \"data\": {\n        \"id\": 88,\n        \"name\": \"Clinic fit-out\",\n        \"pipeline\": {\n            \"id\": 1,\n            \"name\": \"Sales pipeline\"\n        },\n        \"stage\": {\n            \"id\": 3,\n            \"name\": \"Qualified\"\n        },\n        \"status\": \"open\",\n        \"amount\": \"25000.00\",\n        \"close_date\": null,\n        \"owner_id\": 7,\n        \"organization_id\": null,\n        \"source\": \"website\",\n        \"closed_at\": null,\n        \"closed_lost_reason\": null,\n        \"contact_ids\": [\n            42\n        ],\n        \"created_at\": \"2026-07-26T09:41:00+00:00\"\n    }\n}","_postman_previewlanguage":"json"}]},{"name":"Change a deal","request":{"method":"PATCH","header":[{"key":"Accept","value":"application/json"},{"key":"Content-Type","value":"application/json"},{"key":"Idempotency-Key","value":"{{$guid}}","description":"Any unique string of your own. Retrying with the same key returns the first response instead of repeating the work. Remembered for 24 hours."}],"url":{"raw":"{{base_url}}/deals/{{deal_id}}","host":["{{base_url}}"],"path":["deals","{{deal_id}}"]},"description":"Changes a deal's own facts — only the fields you send are touched.\n\n**The stage is the deal's verdict.** Send `stage_id` to move it along its\npipeline: the Won stage wins it, the Lost stage loses it (send\n`closed_lost_reason` with it to say why), and moving back to an open stage\nreopens it. There is no separate close call.\n\n**Required ability:** `deals:write`","body":{"mode":"raw","raw":"{\n    \"source\": \"referral\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PATCH","header":[{"key":"Accept","value":"application/json"},{"key":"Content-Type","value":"application/json"},{"key":"Idempotency-Key","value":"{{$guid}}","description":"Any unique string of your own. Retrying with the same key returns the first response instead of repeating the work. Remembered for 24 hours."}],"url":{"raw":"{{base_url}}/deals/{{deal_id}}","host":["{{base_url}}"],"path":["deals","{{deal_id}}"]},"description":"Changes a deal's own facts — only the fields you send are touched.\n\n**The stage is the deal's verdict.** Send `stage_id` to move it along its\npipeline: the Won stage wins it, the Lost stage loses it (send\n`closed_lost_reason` with it to say why), and moving back to an open stage\nreopens it. There is no separate close call.\n\n**Required ability:** `deals:write`","body":{"mode":"raw","raw":"{\n    \"source\": \"referral\"\n}","options":{"raw":{"language":"json"}}}},"status":"OK","code":200,"header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n    \"data\": {\n        \"id\": 88,\n        \"name\": \"Clinic fit-out\",\n        \"pipeline\": {\n            \"id\": 1,\n            \"name\": \"Sales pipeline\"\n        },\n        \"stage\": {\n            \"id\": 3,\n            \"name\": \"Qualified\"\n        },\n        \"status\": \"open\",\n        \"amount\": \"25000.00\",\n        \"close_date\": null,\n        \"owner_id\": 7,\n        \"organization_id\": null,\n        \"source\": \"website\",\n        \"closed_at\": null,\n        \"closed_lost_reason\": null,\n        \"contact_ids\": [\n            42\n        ],\n        \"created_at\": \"2026-07-26T09:41:00+00:00\"\n    }\n}","_postman_previewlanguage":"json"}]}]},{"name":"Orders","description":"What a customer bought — lines, delivery, payment, and where it is on the way.","item":[{"name":"List orders","request":{"method":"GET","header":[{"key":"Accept","value":"application/json"}],"url":{"raw":"{{base_url}}/orders","host":["{{base_url}}"],"path":["orders"],"query":[{"key":"status","value":"","description":"","disabled":true},{"key":"payment_status","value":"","description":"","disabled":true},{"key":"contact_id","value":"","description":"","disabled":true},{"key":"since","value":"","description":"","disabled":true},{"key":"per_page","value":"","description":"Rows per page. Defaults to 50, capped at 100.","disabled":true},{"key":"cursor","value":"","description":"The `meta.next_cursor` from the previous page. Opaque — pass it back unchanged.","disabled":true}]},"description":"Orders newest first. `status` takes one status or `open` (anything not delivered, cancelled or returned); `since` is an ISO time compared to `updated_at`, for polling.\n\n**Required ability:** `orders:read`"},"response":[{"name":"OK","originalRequest":{"method":"GET","header":[{"key":"Accept","value":"application/json"}],"url":{"raw":"{{base_url}}/orders","host":["{{base_url}}"],"path":["orders"],"query":[{"key":"status","value":"","description":"","disabled":true},{"key":"payment_status","value":"","description":"","disabled":true},{"key":"contact_id","value":"","description":"","disabled":true},{"key":"since","value":"","description":"","disabled":true},{"key":"per_page","value":"","description":"Rows per page. Defaults to 50, capped at 100.","disabled":true},{"key":"cursor","value":"","description":"The `meta.next_cursor` from the previous page. Opaque — pass it back unchanged.","disabled":true}]},"description":"Orders newest first. `status` takes one status or `open` (anything not delivered, cancelled or returned); `since` is an ISO time compared to `updated_at`, for polling.\n\n**Required ability:** `orders:read`"},"status":"OK","code":200,"header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n    \"data\": [\n        {\n            \"id\": 310,\n            \"number\": 1001,\n            \"contact_id\": 42,\n            \"conversation_id\": 918,\n            \"deal_id\": null,\n            \"owner_id\": 7,\n            \"status\": \"confirmed\",\n            \"source\": \"ai\",\n            \"payment\": {\n                \"method\": \"cod\",\n                \"status\": \"unpaid\",\n                \"reference\": null,\n                \"paid_at\": null\n            },\n            \"delivery\": {\n                \"address\": \"شارع ٩، المعادي\",\n                \"city\": \"القاهرة\",\n                \"area\": \"المعادي\",\n                \"fee\": \"50.00\"\n            },\n            \"items\": [\n                {\n                    \"id\": 1,\n                    \"product_id\": 5,\n                    \"product_variant_id\": 12,\n                    \"name\": \"سويتر تريكو — أحمر / L\",\n                    \"sku\": \"SW-R-L\",\n                    \"quantity\": 2,\n                    \"unit_price\": \"450.00\",\n                    \"line_total\": \"900.00\"\n                }\n            ],\n            \"subtotal\": \"900.00\",\n            \"discount\": \"0.00\",\n            \"total\": \"950.00\",\n            \"notes\": \"بعد الساعة ٥\",\n            \"external_id\": null,\n            \"confirmed_at\": \"2026-08-29T10:05:00+00:00\",\n            \"delivered_at\": null,\n            \"cancelled_at\": null,\n            \"cancel_reason\": null,\n            \"created_at\": \"2026-08-29T10:00:00+00:00\",\n            \"updated_at\": \"2026-08-29T10:05:00+00:00\"\n        }\n    ],\n    \"meta\": {\n        \"next_cursor\": null,\n        \"per_page\": 50\n    }\n}","_postman_previewlanguage":"json"}]},{"name":"Place an order","request":{"method":"POST","header":[{"key":"Accept","value":"application/json"},{"key":"Content-Type","value":"application/json"},{"key":"Idempotency-Key","value":"{{$guid}}","description":"Any unique string of your own. Retrying with the same key returns the first response instead of repeating the work. Remembered for 24 hours."}],"url":{"raw":"{{base_url}}/orders","host":["{{base_url}}"],"path":["orders"]},"description":"Places an order in `new`. Each line names a `product_id` or `product_variant_id` from the catalogue (name and price are snapshotted), or is free text with its own `unit_price`. The delivery fee is whatever `delivery_fee` says (0 when omitted). `external_id` makes the call safe to repeat: a second order with the same one answers `422`.\n\n**Required ability:** `orders:write`","body":{"mode":"raw","raw":"{\n    \"contact_id\": 42,\n    \"items\": [\n        {\n            \"product_variant_id\": 12,\n            \"quantity\": 2\n        }\n    ],\n    \"delivery_address\": \"شارع ٩، المعادي\",\n    \"delivery_area\": \"المعادي\",\n    \"payment_method\": \"cod\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"Created","originalRequest":{"method":"POST","header":[{"key":"Accept","value":"application/json"},{"key":"Content-Type","value":"application/json"},{"key":"Idempotency-Key","value":"{{$guid}}","description":"Any unique string of your own. Retrying with the same key returns the first response instead of repeating the work. Remembered for 24 hours."}],"url":{"raw":"{{base_url}}/orders","host":["{{base_url}}"],"path":["orders"]},"description":"Places an order in `new`. Each line names a `product_id` or `product_variant_id` from the catalogue (name and price are snapshotted), or is free text with its own `unit_price`. The delivery fee is whatever `delivery_fee` says (0 when omitted). `external_id` makes the call safe to repeat: a second order with the same one answers `422`.\n\n**Required ability:** `orders:write`","body":{"mode":"raw","raw":"{\n    \"contact_id\": 42,\n    \"items\": [\n        {\n            \"product_variant_id\": 12,\n            \"quantity\": 2\n        }\n    ],\n    \"delivery_address\": \"شارع ٩، المعادي\",\n    \"delivery_area\": \"المعادي\",\n    \"payment_method\": \"cod\"\n}","options":{"raw":{"language":"json"}}}},"status":"Created","code":201,"header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n    \"data\": {\n        \"id\": 310,\n        \"number\": 1001,\n        \"contact_id\": 42,\n        \"conversation_id\": 918,\n        \"deal_id\": null,\n        \"owner_id\": 7,\n        \"status\": \"confirmed\",\n        \"source\": \"ai\",\n        \"payment\": {\n            \"method\": \"cod\",\n            \"status\": \"unpaid\",\n            \"reference\": null,\n            \"paid_at\": null\n        },\n        \"delivery\": {\n            \"address\": \"شارع ٩، المعادي\",\n            \"city\": \"القاهرة\",\n            \"area\": \"المعادي\",\n            \"fee\": \"50.00\"\n        },\n        \"items\": [\n            {\n                \"id\": 1,\n                \"product_id\": 5,\n                \"product_variant_id\": 12,\n                \"name\": \"سويتر تريكو — أحمر / L\",\n                \"sku\": \"SW-R-L\",\n                \"quantity\": 2,\n                \"unit_price\": \"450.00\",\n                \"line_total\": \"900.00\"\n            }\n        ],\n        \"subtotal\": \"900.00\",\n        \"discount\": \"0.00\",\n        \"total\": \"950.00\",\n        \"notes\": \"بعد الساعة ٥\",\n        \"external_id\": null,\n        \"confirmed_at\": \"2026-08-29T10:05:00+00:00\",\n        \"delivered_at\": null,\n        \"cancelled_at\": null,\n        \"cancel_reason\": null,\n        \"created_at\": \"2026-08-29T10:00:00+00:00\",\n        \"updated_at\": \"2026-08-29T10:05:00+00:00\"\n    }\n}","_postman_previewlanguage":"json"}]},{"name":"Get an order","request":{"method":"GET","header":[{"key":"Accept","value":"application/json"}],"url":{"raw":"{{base_url}}/orders/{{order_id}}","host":["{{base_url}}"],"path":["orders","{{order_id}}"]},"description":"One order by id, with its lines.\n\n**Required ability:** `orders:read`"},"response":[{"name":"OK","originalRequest":{"method":"GET","header":[{"key":"Accept","value":"application/json"}],"url":{"raw":"{{base_url}}/orders/{{order_id}}","host":["{{base_url}}"],"path":["orders","{{order_id}}"]},"description":"One order by id, with its lines.\n\n**Required ability:** `orders:read`"},"status":"OK","code":200,"header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n    \"data\": {\n        \"id\": 310,\n        \"number\": 1001,\n        \"contact_id\": 42,\n        \"conversation_id\": 918,\n        \"deal_id\": null,\n        \"owner_id\": 7,\n        \"status\": \"confirmed\",\n        \"source\": \"ai\",\n        \"payment\": {\n            \"method\": \"cod\",\n            \"status\": \"unpaid\",\n            \"reference\": null,\n            \"paid_at\": null\n        },\n        \"delivery\": {\n            \"address\": \"شارع ٩، المعادي\",\n            \"city\": \"القاهرة\",\n            \"area\": \"المعادي\",\n            \"fee\": \"50.00\"\n        },\n        \"items\": [\n            {\n                \"id\": 1,\n                \"product_id\": 5,\n                \"product_variant_id\": 12,\n                \"name\": \"سويتر تريكو — أحمر / L\",\n                \"sku\": \"SW-R-L\",\n                \"quantity\": 2,\n                \"unit_price\": \"450.00\",\n                \"line_total\": \"900.00\"\n            }\n        ],\n        \"subtotal\": \"900.00\",\n        \"discount\": \"0.00\",\n        \"total\": \"950.00\",\n        \"notes\": \"بعد الساعة ٥\",\n        \"external_id\": null,\n        \"confirmed_at\": \"2026-08-29T10:05:00+00:00\",\n        \"delivered_at\": null,\n        \"cancelled_at\": null,\n        \"cancel_reason\": null,\n        \"created_at\": \"2026-08-29T10:00:00+00:00\",\n        \"updated_at\": \"2026-08-29T10:05:00+00:00\"\n    }\n}","_postman_previewlanguage":"json"}]},{"name":"Change an order","request":{"method":"PATCH","header":[{"key":"Accept","value":"application/json"},{"key":"Content-Type","value":"application/json"},{"key":"Idempotency-Key","value":"{{$guid}}","description":"Any unique string of your own. Retrying with the same key returns the first response instead of repeating the work. Remembered for 24 hours."}],"url":{"raw":"{{base_url}}/orders/{{order_id}}","host":["{{base_url}}"],"path":["orders","{{order_id}}"]},"description":"Changes delivery, payment, notes, discount or owner — only the fields you send. The status is not a field: see the status call.\n\n**Required ability:** `orders:write`","body":{"mode":"raw","raw":"{\n    \"payment_method\": \"bank_transfer\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PATCH","header":[{"key":"Accept","value":"application/json"},{"key":"Content-Type","value":"application/json"},{"key":"Idempotency-Key","value":"{{$guid}}","description":"Any unique string of your own. Retrying with the same key returns the first response instead of repeating the work. Remembered for 24 hours."}],"url":{"raw":"{{base_url}}/orders/{{order_id}}","host":["{{base_url}}"],"path":["orders","{{order_id}}"]},"description":"Changes delivery, payment, notes, discount or owner — only the fields you send. The status is not a field: see the status call.\n\n**Required ability:** `orders:write`","body":{"mode":"raw","raw":"{\n    \"payment_method\": \"bank_transfer\"\n}","options":{"raw":{"language":"json"}}}},"status":"OK","code":200,"header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n    \"data\": {\n        \"id\": 310,\n        \"number\": 1001,\n        \"contact_id\": 42,\n        \"conversation_id\": 918,\n        \"deal_id\": null,\n        \"owner_id\": 7,\n        \"status\": \"confirmed\",\n        \"source\": \"ai\",\n        \"payment\": {\n            \"method\": \"cod\",\n            \"status\": \"unpaid\",\n            \"reference\": null,\n            \"paid_at\": null\n        },\n        \"delivery\": {\n            \"address\": \"شارع ٩، المعادي\",\n            \"city\": \"القاهرة\",\n            \"area\": \"المعادي\",\n            \"fee\": \"50.00\"\n        },\n        \"items\": [\n            {\n                \"id\": 1,\n                \"product_id\": 5,\n                \"product_variant_id\": 12,\n                \"name\": \"سويتر تريكو — أحمر / L\",\n                \"sku\": \"SW-R-L\",\n                \"quantity\": 2,\n                \"unit_price\": \"450.00\",\n                \"line_total\": \"900.00\"\n            }\n        ],\n        \"subtotal\": \"900.00\",\n        \"discount\": \"0.00\",\n        \"total\": \"950.00\",\n        \"notes\": \"بعد الساعة ٥\",\n        \"external_id\": null,\n        \"confirmed_at\": \"2026-08-29T10:05:00+00:00\",\n        \"delivered_at\": null,\n        \"cancelled_at\": null,\n        \"cancel_reason\": null,\n        \"created_at\": \"2026-08-29T10:00:00+00:00\",\n        \"updated_at\": \"2026-08-29T10:05:00+00:00\"\n    }\n}","_postman_previewlanguage":"json"}]},{"name":"Move an order","request":{"method":"PATCH","header":[{"key":"Accept","value":"application/json"},{"key":"Content-Type","value":"application/json"},{"key":"Idempotency-Key","value":"{{$guid}}","description":"Any unique string of your own. Retrying with the same key returns the first response instead of repeating the work. Remembered for 24 hours."}],"url":{"raw":"{{base_url}}/orders/{{order_id}}/status","host":["{{base_url}}"],"path":["orders","{{order_id}}","status"]},"description":"Moves the order to another status — only the steps its current status allows: `new → confirmed → preparing → out_for_delivery → delivered`, `cancelled` from any open status, `returned` after delivery. Confirming reserves stock for every tracked line and answers `422` on a shortage unless `force` is true; cancelling releases it, delivering sells it, a return restocks it.\n\n**Required ability:** `orders:write`","body":{"mode":"raw","raw":"{\n    \"status\": \"confirmed\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PATCH","header":[{"key":"Accept","value":"application/json"},{"key":"Content-Type","value":"application/json"},{"key":"Idempotency-Key","value":"{{$guid}}","description":"Any unique string of your own. Retrying with the same key returns the first response instead of repeating the work. Remembered for 24 hours."}],"url":{"raw":"{{base_url}}/orders/{{order_id}}/status","host":["{{base_url}}"],"path":["orders","{{order_id}}","status"]},"description":"Moves the order to another status — only the steps its current status allows: `new → confirmed → preparing → out_for_delivery → delivered`, `cancelled` from any open status, `returned` after delivery. Confirming reserves stock for every tracked line and answers `422` on a shortage unless `force` is true; cancelling releases it, delivering sells it, a return restocks it.\n\n**Required ability:** `orders:write`","body":{"mode":"raw","raw":"{\n    \"status\": \"confirmed\"\n}","options":{"raw":{"language":"json"}}}},"status":"OK","code":200,"header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n    \"data\": {\n        \"id\": 310,\n        \"number\": 1001,\n        \"contact_id\": 42,\n        \"conversation_id\": 918,\n        \"deal_id\": null,\n        \"owner_id\": 7,\n        \"status\": \"confirmed\",\n        \"source\": \"ai\",\n        \"payment\": {\n            \"method\": \"cod\",\n            \"status\": \"unpaid\",\n            \"reference\": null,\n            \"paid_at\": null\n        },\n        \"delivery\": {\n            \"address\": \"شارع ٩، المعادي\",\n            \"city\": \"القاهرة\",\n            \"area\": \"المعادي\",\n            \"fee\": \"50.00\"\n        },\n        \"items\": [\n            {\n                \"id\": 1,\n                \"product_id\": 5,\n                \"product_variant_id\": 12,\n                \"name\": \"سويتر تريكو — أحمر / L\",\n                \"sku\": \"SW-R-L\",\n                \"quantity\": 2,\n                \"unit_price\": \"450.00\",\n                \"line_total\": \"900.00\"\n            }\n        ],\n        \"subtotal\": \"900.00\",\n        \"discount\": \"0.00\",\n        \"total\": \"950.00\",\n        \"notes\": \"بعد الساعة ٥\",\n        \"external_id\": null,\n        \"confirmed_at\": \"2026-08-29T10:05:00+00:00\",\n        \"delivered_at\": null,\n        \"cancelled_at\": null,\n        \"cancel_reason\": null,\n        \"created_at\": \"2026-08-29T10:00:00+00:00\",\n        \"updated_at\": \"2026-08-29T10:05:00+00:00\"\n    }\n}","_postman_previewlanguage":"json"}]}]},{"name":"Products","description":"The catalogue an order's lines refer to, read-only.","item":[{"name":"List products","request":{"method":"GET","header":[{"key":"Accept","value":"application/json"}],"url":{"raw":"{{base_url}}/products","host":["{{base_url}}"],"path":["products"],"query":[{"key":"active","value":"","description":"","disabled":true},{"key":"search","value":"","description":"","disabled":true},{"key":"per_page","value":"","description":"Rows per page. Defaults to 50, capped at 100.","disabled":true},{"key":"cursor","value":"","description":"The `meta.next_cursor` from the previous page. Opaque — pass it back unchanged.","disabled":true}]},"description":"The catalogue with variants, prices and what is available to sell. `available` is `null` for products that do not track stock.\n\n**Required ability:** `products:read`"},"response":[{"name":"OK","originalRequest":{"method":"GET","header":[{"key":"Accept","value":"application/json"}],"url":{"raw":"{{base_url}}/products","host":["{{base_url}}"],"path":["products"],"query":[{"key":"active","value":"","description":"","disabled":true},{"key":"search","value":"","description":"","disabled":true},{"key":"per_page","value":"","description":"Rows per page. Defaults to 50, capped at 100.","disabled":true},{"key":"cursor","value":"","description":"The `meta.next_cursor` from the previous page. Opaque — pass it back unchanged.","disabled":true}]},"description":"The catalogue with variants, prices and what is available to sell. `available` is `null` for products that do not track stock.\n\n**Required ability:** `products:read`"},"status":"OK","code":200,"header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n    \"data\": [\n        {\n            \"id\": 5,\n            \"name\": \"سويتر تريكو\",\n            \"description\": null,\n            \"sku\": \"SW\",\n            \"price\": \"450.00\",\n            \"is_active\": true,\n            \"track_stock\": true,\n            \"available\": 7,\n            \"image_url\": null,\n            \"variants\": [\n                {\n                    \"id\": 12,\n                    \"name\": \"أحمر / L\",\n                    \"options\": {\n                        \"colour\": \"أحمر\",\n                        \"size\": \"L\"\n                    },\n                    \"sku\": \"SW-R-L\",\n                    \"price\": \"450.00\",\n                    \"available\": 3,\n                    \"is_active\": true\n                }\n            ],\n            \"created_at\": \"2026-08-29T09:00:00+00:00\"\n        }\n    ],\n    \"meta\": {\n        \"next_cursor\": null,\n        \"per_page\": 50\n    }\n}","_postman_previewlanguage":"json"}]},{"name":"Get a product","request":{"method":"GET","header":[{"key":"Accept","value":"application/json"}],"url":{"raw":"{{base_url}}/products/{{product_id}}","host":["{{base_url}}"],"path":["products","{{product_id}}"]},"description":"One product by id, with its variants.\n\n**Required ability:** `products:read`"},"response":[{"name":"OK","originalRequest":{"method":"GET","header":[{"key":"Accept","value":"application/json"}],"url":{"raw":"{{base_url}}/products/{{product_id}}","host":["{{base_url}}"],"path":["products","{{product_id}}"]},"description":"One product by id, with its variants.\n\n**Required ability:** `products:read`"},"status":"OK","code":200,"header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n    \"data\": {\n        \"id\": 5,\n        \"name\": \"سويتر تريكو\",\n        \"description\": null,\n        \"sku\": \"SW\",\n        \"price\": \"450.00\",\n        \"is_active\": true,\n        \"track_stock\": true,\n        \"available\": 7,\n        \"image_url\": null,\n        \"variants\": [\n            {\n                \"id\": 12,\n                \"name\": \"أحمر / L\",\n                \"options\": {\n                    \"colour\": \"أحمر\",\n                    \"size\": \"L\"\n                },\n                \"sku\": \"SW-R-L\",\n                \"price\": \"450.00\",\n                \"available\": 3,\n                \"is_active\": true\n            }\n        ],\n        \"created_at\": \"2026-08-29T09:00:00+00:00\"\n    }\n}","_postman_previewlanguage":"json"}]}]},{"name":"Tasks","description":"Work the team owes somebody, with an optional contact attached.","item":[{"name":"List tasks","request":{"method":"GET","header":[{"key":"Accept","value":"application/json"}],"url":{"raw":"{{base_url}}/tasks","host":["{{base_url}}"],"path":["tasks"],"query":[{"key":"status","value":"","description":"","disabled":true},{"key":"assignee_id","value":"","description":"","disabled":true},{"key":"contact_id","value":"","description":"","disabled":true},{"key":"due_before","value":"","description":"Tasks due at or before this moment.","disabled":true},{"key":"per_page","value":"","description":"Rows per page. Defaults to 50, capped at 100.","disabled":true},{"key":"cursor","value":"","description":"The `meta.next_cursor` from the previous page. Opaque — pass it back unchanged.","disabled":true}]},"description":"Work the team owes somebody, soonest first. Filter by `status`, `assignee_id`, `contact_id` or `due_before`.\n\n**Required ability:** `tasks:read`"},"response":[{"name":"OK","originalRequest":{"method":"GET","header":[{"key":"Accept","value":"application/json"}],"url":{"raw":"{{base_url}}/tasks","host":["{{base_url}}"],"path":["tasks"],"query":[{"key":"status","value":"","description":"","disabled":true},{"key":"assignee_id","value":"","description":"","disabled":true},{"key":"contact_id","value":"","description":"","disabled":true},{"key":"due_before","value":"","description":"Tasks due at or before this moment.","disabled":true},{"key":"per_page","value":"","description":"Rows per page. Defaults to 50, capped at 100.","disabled":true},{"key":"cursor","value":"","description":"The `meta.next_cursor` from the previous page. Opaque — pass it back unchanged.","disabled":true}]},"description":"Work the team owes somebody, soonest first. Filter by `status`, `assignee_id`, `contact_id` or `due_before`.\n\n**Required ability:** `tasks:read`"},"status":"OK","code":200,"header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n    \"data\": [\n        {\n            \"id\": 21,\n            \"title\": \"Call Mona back\",\n            \"description\": null,\n            \"type\": \"call\",\n            \"status\": \"open\",\n            \"due_at\": \"2026-08-06T10:00:00+00:00\",\n            \"completed_at\": null,\n            \"assignee_id\": 7,\n            \"created_by\": 7,\n            \"contact_id\": 42\n        }\n    ],\n    \"meta\": {\n        \"next_cursor\": null,\n        \"per_page\": 50\n    }\n}","_postman_previewlanguage":"json"}]},{"name":"Create a task","request":{"method":"POST","header":[{"key":"Accept","value":"application/json"},{"key":"Content-Type","value":"application/json"},{"key":"Idempotency-Key","value":"{{$guid}}","description":"Any unique string of your own. Retrying with the same key returns the first response instead of repeating the work. Remembered for 24 hours."}],"url":{"raw":"{{base_url}}/tasks","host":["{{base_url}}"],"path":["tasks"]},"description":"Adds a task. Give a `contact_id` and it hangs off that person's record and shows on the calendar, which is how \"call Mona back on Thursday\" ends up in front of the right teammate. Unassigned tasks go to the token's own teammate rather than nobody.\n\n**Required ability:** `tasks:write`","body":{"mode":"raw","raw":"{\n    \"title\": \"Call Mona back\",\n    \"due_at\": \"2026-08-06T10:00:00+00:00\"\n}","options":{"raw":{"language":"json"}}}},"response":[{"name":"Created","originalRequest":{"method":"POST","header":[{"key":"Accept","value":"application/json"},{"key":"Content-Type","value":"application/json"},{"key":"Idempotency-Key","value":"{{$guid}}","description":"Any unique string of your own. Retrying with the same key returns the first response instead of repeating the work. Remembered for 24 hours."}],"url":{"raw":"{{base_url}}/tasks","host":["{{base_url}}"],"path":["tasks"]},"description":"Adds a task. Give a `contact_id` and it hangs off that person's record and shows on the calendar, which is how \"call Mona back on Thursday\" ends up in front of the right teammate. Unassigned tasks go to the token's own teammate rather than nobody.\n\n**Required ability:** `tasks:write`","body":{"mode":"raw","raw":"{\n    \"title\": \"Call Mona back\",\n    \"due_at\": \"2026-08-06T10:00:00+00:00\"\n}","options":{"raw":{"language":"json"}}}},"status":"Created","code":201,"header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n    \"data\": {\n        \"id\": 21,\n        \"title\": \"Call Mona back\",\n        \"description\": null,\n        \"type\": \"call\",\n        \"status\": \"open\",\n        \"due_at\": \"2026-08-06T10:00:00+00:00\",\n        \"completed_at\": null,\n        \"assignee_id\": 7,\n        \"created_by\": 7,\n        \"contact_id\": 42\n    }\n}","_postman_previewlanguage":"json"}]},{"name":"Change a task","request":{"method":"PATCH","header":[{"key":"Accept","value":"application/json"},{"key":"Content-Type","value":"application/json"},{"key":"Idempotency-Key","value":"{{$guid}}","description":"Any unique string of your own. Retrying with the same key returns the first response instead of repeating the work. Remembered for 24 hours."}],"url":{"raw":"{{base_url}}/tasks/{{task_id}}","host":["{{base_url}}"],"path":["tasks","{{task_id}}"]},"description":"Only the fields you send are touched. Moving `status` runs the same transition the panel does, so completing a task stamps when it was completed rather than leaving a done task that never finished.\n\n**Required ability:** `tasks:write`","body":{"mode":"raw","raw":"[]","options":{"raw":{"language":"json"}}}},"response":[{"name":"OK","originalRequest":{"method":"PATCH","header":[{"key":"Accept","value":"application/json"},{"key":"Content-Type","value":"application/json"},{"key":"Idempotency-Key","value":"{{$guid}}","description":"Any unique string of your own. Retrying with the same key returns the first response instead of repeating the work. Remembered for 24 hours."}],"url":{"raw":"{{base_url}}/tasks/{{task_id}}","host":["{{base_url}}"],"path":["tasks","{{task_id}}"]},"description":"Only the fields you send are touched. Moving `status` runs the same transition the panel does, so completing a task stamps when it was completed rather than leaving a done task that never finished.\n\n**Required ability:** `tasks:write`","body":{"mode":"raw","raw":"[]","options":{"raw":{"language":"json"}}}},"status":"OK","code":200,"header":[{"key":"Content-Type","value":"application/json"}],"body":"{\n    \"data\": {\n        \"id\": 21,\n        \"title\": \"Call Mona back\",\n        \"description\": null,\n        \"type\": \"call\",\n        \"status\": \"done\",\n        \"due_at\": \"2026-08-06T10:00:00+00:00\",\n        \"completed_at\": \"2026-08-06T11:04:00+00:00\",\n        \"assignee_id\": 7,\n        \"created_by\": 7,\n        \"contact_id\": 42\n    }\n}","_postman_previewlanguage":"json"}]}]}]}