Customers
Operations tagged Customers in the public API.
About Customers
A customer is one person a chatbot can reach back — by email, phone or your own external_id — kept as one profile per chatbot. Profiles come from chat and from you: the lead form of the widget and the hosted agent page (source: widget), the dashboard Test Chat (dashboard), a collect_info Call Action (action), a visitor message that is only contact details (chat_intent), and POST /customers (api).
Identity. A contact is matched within its chatbot on external_id, then email (compared lower-cased), then phone (compared in E.164), so the same person on two chatbots is two customers. A match fills the profile's empty fields and never overwrites one. Nothing is verified: a visitor can type anyone's email. The name and id a host page passes to the widget (visitor) are display data on the conversation — they never match or create a customer, and a customer is never a login.
Personal data. full_name, email, phone and company are personal data, and so are the conversations behind a profile. POST /customers/{id}/anonymize erases them for a data-subject request and keeps the counts; DELETE /customers/{id} removes the profile but keeps its conversations. The CSV export needs customers:write.
Lifecycle. A profile starts at lead_status: new and lifecycle_stage: lead (visitor while it has no contact); moving it along is yours, with PATCH /customers/{id}. Every capture from chat emits lead.captured, plus customer.created or customer.updated; the API's own writes emit customer.created, customer.updated and customer.deleted. Anonymization emits nothing.
IDs you will meet.
id— the customer id, an integer, in every/customers/{id}path and in the bulk operations'ids.chatbot_id— the chatbot the profile belongs to.external_id— your own identifier, as you sent it; the strongest match key.- Timeline
chat_session_id— the conversation id of the chat the event happened in, forGET /conversations/{id}.
IDs on this page
- Chatbot id
<CHATBOT_ID> - Integer.
Get it from
- GET /v1/chatbots returns it as
data[].id - POST /v1/chatbots returns it as
id - GET /v1/chatbots/by_handle/{handle} returns it as
id - Webhook chatbot.created carries it as
data.object.id - In the dashboard, it is the number in a chatbot's address:
/dashboard/chatbots/{id}.
- GET /v1/chatbots returns it as
- Customer id
<CUSTOMER_ID> - Integer.
Get it from
- GET /v1/customers returns it as
data[].id - POST /v1/customers returns it as
id - Webhook customer.created carries it as
data.object.id - Webhook lead.captured carries it as
data.object.id lead.capturednames the customer the lead was filed under: itsdata.objectis{"object": "lead", "id": <customer id>}. In the dashboard, the id is the number in a customer's address:/dashboard/customers/{id}.
- GET /v1/customers returns it as
- Customer ids
<CUSTOMER_IDS> - Array of 1–200 integer customer ids. A repeated id counts once; an id the API key cannot reach is skipped, not an error.
Get it from
- GET /v1/customers returns it as
data[].id - POST /v1/customers returns it as
id - Webhook customer.created carries it as
data.object.id - Webhook lead.captured carries it as
data.object.id lead.capturednames the customer the lead was filed under: itsdata.objectis{"object": "lead", "id": <customer id>}. In the dashboard, the id is the number in a customer's address:/dashboard/customers/{id}.
- GET /v1/customers returns it as
List customers
chatbot_id, status (the lead_status), lifecycle_stage, source, utm_source, a search over name, company, email and phone, and two creation-date windows — date_from / date_to (whole UTC days) and the shared created[gte] / created[lte].
sort takes id (the default), created_at or last_seen_at — the last time the person was captured or matched, which the customer object does not return — with a + prefix or order=asc for oldest first.- Required scope
- customers:read
- Idempotency
- Read-only
- Rate limits
Integer.chatbot_id(query)Chatbot idPlaceholder in the samples:
<CHATBOT_ID>Get it from
- GET /v1/chatbots returns it as
data[].id - POST /v1/chatbots returns it as
id - GET /v1/chatbots/by_handle/{handle} returns it as
id - Webhook chatbot.created carries it as
data.object.id - In the dashboard, it is the number in a chatbot's address:
/dashboard/chatbots/{id}.
- GET /v1/chatbots returns it as
Query parameters
searchstringnullableOptionalText the customer's name, company, email or phone contains. At most 255 characters.Max length:
255chatbot_idintegernullableOptionalOnly this chatbot's customers. A chatbot the API key cannot reach matches nothing: the answer is an empty list, not 404.statusstringnullableOptionalOnly customers with thislead_status.Possible values
newcontactedqualifiedunqualifiedconvertedblockednulllifecycle_stagestringnullableOptionalOnly customers at thislifecycle_stage.Possible values
visitorleadcustomervipnullsourcestringnullableOptionalOnly customers first captured from thissource:widget,dashboard,action,chat_intentorapi. Exact match.Max length:
255utm_sourcestringnullableOptionalOnly customers whose captured visit carried thisutm_sourcetag. Exact match; only the widget and hosted agent page lead forms record UTM tags.Max length:
255date_fromstringnullableOptionalEarliest creation day, inclusive: Y-m-d or an RFC 3339 date-time (YYYY-MM-DDThh:mm:ss.ffffff). An instant counts as the UTC calendar day it falls on.Example:
"2026-10-01"Format:date-timedate_tostringnullableOptionalLatest creation day, inclusive: Y-m-d or an RFC 3339 date-time (YYYY-MM-DDThh:mm:ss.ffffff). An instant counts as the UTC calendar day it falls on.Example:
"2026-10-31T23:59:59Z"Format:date-timelimitintegerOptionalPage size, clamped to 1–100.Default:
20Minimum:1Maximum:100starting_afterstringOptionalCursor from a previous page'snext_cursor: returns the page after it. A cursor minted for another list,sortororderis 422INVALID_CURSOR.ending_beforestringOptionalCursor from a previous page'sprevious_cursor: returns the page before it. Wins when both cursors are sent.created[gte]stringOptionalOnly rows created on or after this instant:YYYY-MM-DD(whole UTC day) or an RFC 3339 timestamp with a zone. Anything else is 422INVALID_PARAMETER.created[lte]stringOptionalOnly rows created on or before this instant:YYYY-MM-DD(whole UTC day) or an RFC 3339 timestamp with a zone. Anything else is 422INVALID_PARAMETER.
Responses
- 200Success.
objectstringRequiredAlways
listdataarray of CustomerResourceRequiredhas_morebooleanRequirednext_cursorstringnullableRequiredprevious_cursorstringnullableRequiredurlstringRequiredAlways
/v1/customers
- 400A tenant selector header named a tenant other than the API key's own (
ACCOUNT_SELECTOR_NOT_SUPPORTED): an all-digitX-Account-Idother than the key owner's id or, whileworkspaces.context_enabledis on, anX-Workspace-Idother than the key's workspace (ws_prefix optional, case-insensitive). A header naming the key's own tenant, or a non-numericX-Account-Id, is ignored; a key cannot switch tenants, so send neither.Error codesACCOUNT_SELECTOR_NOT_SUPPORTED
- 401Missing or invalid API key.
Error codesUNAUTHENTICATED
- 403The API key is missing a required scope.
Error codesACCOUNT_SUSPENDEDINSUFFICIENT_SCOPE
- 422Validation failed. See
error.details. - 429Rate limited. Honour the Retry-After header before retrying.
Error codesRATE_LIMITED
- 500Unexpected server error. Retry with the same Idempotency-Key.
Error codesSERVER_ERROR
Request
curl -X GET 'https://api.agentency.com/v1/customers' \
-H "Authorization: Bearer <YOUR_KEY>" \
-H "Content-Type: application/json"Response
Example 200 response
{
"object": "list",
"data": [
{
"object": "customer",
"id": 1,
"chatbot_id": 1,
"external_id": "crm_1042",
"full_name": "Dana Whitfield",
"email": "dana.whitfield@example.com",
"phone": "+442071838750",
"company": "Northwind Traders",
"lead_status": "qualified",
"lifecycle_stage": "lead",
"source": "widget",
"created_at": "2026-01-01T00:00:00+00:00",
"updated_at": "2026-01-01T00:00:00+00:00"
}
],
"has_more": false,
"next_cursor": null,
"previous_cursor": null,
"url": "/v1/customers"
}Create or match a customer
source: api. Send email, phone or both; at least one must be valid.
Customers are deduplicated per chatbot on external_id, email and phone, in that order of authority. When the contact matches an existing customer, the submitted fields fill that customer's empty fields (non-empty values are never overwritten) and the call answers 200 with the existing customer and emits customer.updated; a new customer answers 201 and emits customer.created. Neither emits lead.captured, which is for captures from chat.- Required scope
- customers:write
- Idempotency
- Accepts Idempotency-Key
- Webhook events
- customer.createdcustomer.updated
- Rate limits
Integer.chatbot_id(body)Chatbot idPlaceholder in the samples:
<CHATBOT_ID>Get it from
- GET /v1/chatbots returns it as
data[].id - POST /v1/chatbots returns it as
id - GET /v1/chatbots/by_handle/{handle} returns it as
id - Webhook chatbot.created carries it as
data.object.id - In the dashboard, it is the number in a chatbot's address:
/dashboard/chatbots/{id}.
- GET /v1/chatbots returns it as
Header parameters
Idempotency-KeystringOptionalMakes a retry safe. The first 2xx answer is stored for 24 h and replayed for the same key withIdempotency-Replayed: true; a retry while the first request runs is 409IDEMPOTENCY_IN_FLIGHT; the same key with a different payload is 422IDEMPOTENCY_KEY_REUSED; a malformed key is 400INVALID_IDEMPOTENCY_KEY.Min length:
16Max length:128Pattern:^[A-Za-z0-9_-]+$
Request body
chatbot_idintegerRequiredThe chatbot the customer belongs to; contacts are matched within this chatbot only. A chatbot of another account is 422VALIDATION_FAILED.emailstringnullableOptionalEmail address, at most 255 characters. Stored as sent and matched lower-cased. Required withoutphone.Format:
emailMax length:255phonestringnullableOptionalPhone number, at most 32 characters, stored as sent. Send it in E.164 (+14155550123): a number without a country code is read in the platform's default region, and one that cannot be parsed is never matched on. Required withoutemail; with no valid email, an unparseable phone is 422VALIDATION_FAILED.Max length:
32full_namestringnullableOptionalThe person's name, at most 255 characters. On a match it only fills an empty name.Max length:
255companystringnullableOptionalCompany name, at most 255 characters. On a match it only fills an empty company.Max length:
255external_idstringnullableOptionalYour own identifier for the person (a CRM id), at most 128 characters. The strongest match key: a knownexternal_idresolves to its customer before email or phone are compared. On a match it is set only when the customer has none.Max length:
128
Responses
- 200
CustomerResourceobjectstringRequiredAlways
customeridintegerRequiredThe customer id.chatbot_idintegerRequiredThe chatbot the profile belongs to. Profiles are per chatbot: the same person on two chatbots is two customers.external_idstringnullableRequiredYour own identifier for the person, exactly asPOST /customerssent it;nullwhen none, or after anonymization. The strongest match key.full_namestringnullableRequiredName as captured or set;nullwhen unknown, or after anonymization.emailstringnullableRequiredEmail address as captured or sent (matching compares it lower-cased);nullwhen none, or after anonymization. Not verified.phonestringnullableRequiredPhone number as captured or sent (matching compares its E.164 form);nullwhen none, or after anonymization. Not verified.companystringnullableRequiredCompany name;nullwhen unknown, or after anonymization.lead_statusstringRequiredSales pipeline status:newwhen captured, then whatever you or the dashboard set (contacted,qualified,unqualified,converted). Anonymization setsblocked.Possible values
newcontactedqualifiedunqualifiedconvertedblockedlifecycle_stagestringRequiredRelationship stage:visitor(no contact yet),lead(a contact is known — where every new profile with one starts),customerorvip. Only a capture that brings a contact movesvisitortolead; the rest is yours to set.Possible values
visitorleadcustomervipsourcestringnullableRequiredWhere the profile was first captured:widget(the widget or the hosted agent page lead form),dashboard(Test Chat),action(acollect_infoCall Action),chat_intent(a visitor message that was only contact details) orapi(POST /customers). Never changes after creation.created_atstringnullableRequiredWhen the profile was created (ISO 8601, UTC).updated_atstringnullableRequiredWhen the profile last changed (ISO 8601, UTC).
- 201
CustomerResourceobjectstringRequiredAlways
customeridintegerRequiredThe customer id.chatbot_idintegerRequiredThe chatbot the profile belongs to. Profiles are per chatbot: the same person on two chatbots is two customers.external_idstringnullableRequiredYour own identifier for the person, exactly asPOST /customerssent it;nullwhen none, or after anonymization. The strongest match key.full_namestringnullableRequiredName as captured or set;nullwhen unknown, or after anonymization.emailstringnullableRequiredEmail address as captured or sent (matching compares it lower-cased);nullwhen none, or after anonymization. Not verified.phonestringnullableRequiredPhone number as captured or sent (matching compares its E.164 form);nullwhen none, or after anonymization. Not verified.companystringnullableRequiredCompany name;nullwhen unknown, or after anonymization.lead_statusstringRequiredSales pipeline status:newwhen captured, then whatever you or the dashboard set (contacted,qualified,unqualified,converted). Anonymization setsblocked.Possible values
newcontactedqualifiedunqualifiedconvertedblockedlifecycle_stagestringRequiredRelationship stage:visitor(no contact yet),lead(a contact is known — where every new profile with one starts),customerorvip. Only a capture that brings a contact movesvisitortolead; the rest is yours to set.Possible values
visitorleadcustomervipsourcestringnullableRequiredWhere the profile was first captured:widget(the widget or the hosted agent page lead form),dashboard(Test Chat),action(acollect_infoCall Action),chat_intent(a visitor message that was only contact details) orapi(POST /customers). Never changes after creation.created_atstringnullableRequiredWhen the profile was created (ISO 8601, UTC).updated_atstringnullableRequiredWhen the profile last changed (ISO 8601, UTC).
- 400A tenant selector header named a tenant other than the API key's own (
ACCOUNT_SELECTOR_NOT_SUPPORTED): an all-digitX-Account-Idother than the key owner's id or, whileworkspaces.context_enabledis on, anX-Workspace-Idother than the key's workspace (ws_prefix optional, case-insensitive). A header naming the key's own tenant, or a non-numericX-Account-Id, is ignored; a key cannot switch tenants, so send neither. A mutation that honoursIdempotency-Keyalso returnsINVALID_IDEMPOTENCY_KEYwhen that header is not 16–128[A-Za-z0-9_-]characters.Error codesACCOUNT_SELECTOR_NOT_SUPPORTEDINVALID_IDEMPOTENCY_KEY
- 401Missing or invalid API key.
Error codesUNAUTHENTICATED
- 403The API key is missing a required scope.
Error codesACCOUNT_SUSPENDEDINSUFFICIENT_SCOPE
- 404The resource does not exist on this account.
Error codesNOT_FOUND
- 409The request conflicts with the current resource state.
Error codesIDEMPOTENCY_IN_FLIGHT
- 422Validation failed. See
error.details.Error codesIDEMPOTENCY_KEY_REUSEDVALIDATION_FAILED
- 429Rate limited. Honour the Retry-After header before retrying.
Error codesRATE_LIMITED
- 500Unexpected server error. Retry with the same Idempotency-Key.
Error codesSERVER_ERROR
Request
curl -X POST 'https://api.agentency.com/v1/customers' \
-H "Authorization: Bearer <YOUR_KEY>" \
-H "Content-Type: application/json"
-d '{
"chatbot_id": 1,
"email": "email",
"phone": "phone",
"full_name": "full_name",
"company": "company",
"external_id": 1
}'Response
Example 200 response
{
"object": "customer",
"id": 0,
"chatbot_id": 0,
"external_id": "string",
"full_name": "string",
"email": "string",
"phone": "string",
"company": "string",
"lead_status": "new",
"lifecycle_stage": "visitor",
"source": "string",
"created_at": "string",
"updated_at": "string"
}Built from the response schema, not recorded from a live call.
Summarise customers
lead_status and lifecycle_stage — all time — plus how many were created in the last 30 days. It covers every chatbot the API key reaches, or one with chatbot_id. The list filters do not apply here.- Required scope
- customers:read
- Idempotency
- Read-only
- Rate limits
Integer.chatbot_id(query)Chatbot idPlaceholder in the samples:
<CHATBOT_ID>Get it from
- GET /v1/chatbots returns it as
data[].id - POST /v1/chatbots returns it as
id - GET /v1/chatbots/by_handle/{handle} returns it as
id - Webhook chatbot.created carries it as
data.object.id - In the dashboard, it is the number in a chatbot's address:
/dashboard/chatbots/{id}.
- GET /v1/chatbots returns it as
Query parameters
chatbot_idintegernullableOptionalSummarise one chatbot's customers. Omit for every chatbot the API key reaches. A chatbot the API key cannot reach is 404NOT_FOUND.Minimum:
1
Responses
- 200Success.
objectstringRequiredAlways
customer_summaryidintegerRequiredThe account id, the same value asidonGET /me.totalintegerRequiredCustomers in scope, all time.Minimum:
0new_in_rangeintegerRequiredCustomers created since the start of the day 30 days ago (UTC), today included.Minimum:
0convertedintegerRequiredCustomers whoselead_statusisconverted, all time.Minimum:
0conversion_ratenumberRequiredconverted÷total, in percent (0–100, one decimal);0when there are no customers.by_statusobjectRequiredCustomers perlead_status, all time. A status no customer has is absent rather than0.by_stageobjectRequiredCustomers perlifecycle_stage, all time. A stage no customer has is absent rather than0.
- 400A tenant selector header named a tenant other than the API key's own (
ACCOUNT_SELECTOR_NOT_SUPPORTED): an all-digitX-Account-Idother than the key owner's id or, whileworkspaces.context_enabledis on, anX-Workspace-Idother than the key's workspace (ws_prefix optional, case-insensitive). A header naming the key's own tenant, or a non-numericX-Account-Id, is ignored; a key cannot switch tenants, so send neither.Error codesACCOUNT_SELECTOR_NOT_SUPPORTED
- 401Missing or invalid API key.
Error codesUNAUTHENTICATED
- 403The API key is missing a required scope.
Error codesACCOUNT_SUSPENDEDINSUFFICIENT_SCOPE
- 404The resource does not exist on this account.
Error codesNOT_FOUND
- 422Validation failed. See
error.details.Error codesVALIDATION_FAILED
- 429Rate limited. Honour the Retry-After header before retrying.
Error codesRATE_LIMITED
- 500Unexpected server error. Retry with the same Idempotency-Key.
Error codesSERVER_ERROR
Request
curl -X GET 'https://api.agentency.com/v1/customers/summary' \
-H "Authorization: Bearer <YOUR_KEY>" \
-H "Content-Type: application/json"Response
Example 200 response
{
"object": "customer_summary",
"id": 1,
"total": 1,
"new_in_range": 1,
"converted": 0,
"conversion_rate": 0,
"by_status": {
"qualified": 1
},
"by_stage": {
"lead": 1
}
}Retrieve a customer
GET /customers/{id}/timeline. A deleted customer, or one the API key cannot reach, is 404 NOT_FOUND; an anonymized one is returned with its personal fields null.- Required scope
- customers:read
- Idempotency
- Read-only
- Rate limits
Integer.id(path)Customer idPlaceholder in the samples:
<CUSTOMER_ID>Get it from
- GET /v1/customers returns it as
data[].id - POST /v1/customers returns it as
id - Webhook customer.created carries it as
data.object.id - Webhook lead.captured carries it as
data.object.id lead.capturednames the customer the lead was filed under: itsdata.objectis{"object": "lead", "id": <customer id>}. In the dashboard, the id is the number in a customer's address:/dashboard/customers/{id}.
- GET /v1/customers returns it as
Path parameters
idintegerRequiredThe customer id.
Responses
- 200
CustomerResourceobjectstringRequiredAlways
customeridintegerRequiredThe customer id.chatbot_idintegerRequiredThe chatbot the profile belongs to. Profiles are per chatbot: the same person on two chatbots is two customers.external_idstringnullableRequiredYour own identifier for the person, exactly asPOST /customerssent it;nullwhen none, or after anonymization. The strongest match key.full_namestringnullableRequiredName as captured or set;nullwhen unknown, or after anonymization.emailstringnullableRequiredEmail address as captured or sent (matching compares it lower-cased);nullwhen none, or after anonymization. Not verified.phonestringnullableRequiredPhone number as captured or sent (matching compares its E.164 form);nullwhen none, or after anonymization. Not verified.companystringnullableRequiredCompany name;nullwhen unknown, or after anonymization.lead_statusstringRequiredSales pipeline status:newwhen captured, then whatever you or the dashboard set (contacted,qualified,unqualified,converted). Anonymization setsblocked.Possible values
newcontactedqualifiedunqualifiedconvertedblockedlifecycle_stagestringRequiredRelationship stage:visitor(no contact yet),lead(a contact is known — where every new profile with one starts),customerorvip. Only a capture that brings a contact movesvisitortolead; the rest is yours to set.Possible values
visitorleadcustomervipsourcestringnullableRequiredWhere the profile was first captured:widget(the widget or the hosted agent page lead form),dashboard(Test Chat),action(acollect_infoCall Action),chat_intent(a visitor message that was only contact details) orapi(POST /customers). Never changes after creation.created_atstringnullableRequiredWhen the profile was created (ISO 8601, UTC).updated_atstringnullableRequiredWhen the profile last changed (ISO 8601, UTC).
- 400A tenant selector header named a tenant other than the API key's own (
ACCOUNT_SELECTOR_NOT_SUPPORTED): an all-digitX-Account-Idother than the key owner's id or, whileworkspaces.context_enabledis on, anX-Workspace-Idother than the key's workspace (ws_prefix optional, case-insensitive). A header naming the key's own tenant, or a non-numericX-Account-Id, is ignored; a key cannot switch tenants, so send neither.Error codesACCOUNT_SELECTOR_NOT_SUPPORTED
- 401Missing or invalid API key.
Error codesUNAUTHENTICATED
- 403The API key is missing a required scope.
Error codesACCOUNT_SUSPENDEDINSUFFICIENT_SCOPE
- 404The resource does not exist on this account.
Error codesNOT_FOUND
- 429Rate limited. Honour the Retry-After header before retrying.
Error codesRATE_LIMITED
- 500Unexpected server error. Retry with the same Idempotency-Key.
Error codesSERVER_ERROR
Request
curl -X GET 'https://api.agentency.com/v1/customers/<CUSTOMER_ID>' \
-H "Authorization: Bearer <YOUR_KEY>" \
-H "Content-Type: application/json"Response
Example 200 response
{
"object": "customer",
"id": 1,
"chatbot_id": 1,
"external_id": "crm_1042",
"full_name": "Dana Whitfield",
"email": "dana.whitfield@example.com",
"phone": "+442071838750",
"company": "Northwind Traders",
"lead_status": "qualified",
"lifecycle_stage": "lead",
"source": "widget",
"created_at": "2026-01-01T00:00:00+00:00",
"updated_at": "2026-01-01T00:00:00+00:00"
}Update a customer
email, phone, external_id) cannot be changed here. Use it to move a profile along the pipeline (lead_status) or its stage (lifecycle_stage). Every successful call emits customer.updated, even one that changes nothing.- Required scope
- customers:write
- Idempotency
- Accepts Idempotency-Key
- Webhook events
- customer.updated
- Rate limits
Integer.id(path)Customer idPlaceholder in the samples:
<CUSTOMER_ID>Get it from
- GET /v1/customers returns it as
data[].id - POST /v1/customers returns it as
id - Webhook customer.created carries it as
data.object.id - Webhook lead.captured carries it as
data.object.id lead.capturednames the customer the lead was filed under: itsdata.objectis{"object": "lead", "id": <customer id>}. In the dashboard, the id is the number in a customer's address:/dashboard/customers/{id}.
- GET /v1/customers returns it as
Path parameters
idintegerRequiredThe customer id.
Header parameters
Idempotency-KeystringOptionalMakes a retry safe. The first 2xx answer is stored for 24 h and replayed for the same key withIdempotency-Replayed: true; a retry while the first request runs is 409IDEMPOTENCY_IN_FLIGHT; the same key with a different payload is 422IDEMPOTENCY_KEY_REUSED; a malformed key is 400INVALID_IDEMPOTENCY_KEY.Min length:
16Max length:128Pattern:^[A-Za-z0-9_-]+$
Request body
full_namestringnullableOptionalThe person's name, at most 255 characters;nullclears it.Max length:
255companystringnullableOptionalCompany name, at most 255 characters;nullclears it.Max length:
255job_titlestringnullableOptionalJob title, at most 255 characters;nullclears it. Shown in the dashboard; the/v1customer object does not return it.Max length:
255websitestringnullableOptionalWebsite, at most 2048 characters, not checked to be a URL;nullclears it. Shown in the dashboard; the/v1customer object does not return it.Max length:
2048countrystringnullableOptionalCountry, free text up to 100 characters;nullclears it. Shown in the dashboard; the/v1customer object does not return it.Max length:
100lead_statusstringOptionalSales pipeline status. Cannot benull.Possible values
newcontactedqualifiedunqualifiedconvertedblockedlifecycle_stagestringOptionalRelationship stage. Cannot benull.Possible values
visitorleadcustomervipextraobjectOptionalCustom key/value data. Replaces what the customer had, so send every key you want to keep. At most 50 top-level keys, 5 levels of nesting and 16 KB as JSON; larger is 422VALIDATION_FAILED. Shown in the dashboard; the/v1customer object does not return it.
Responses
- 200
CustomerResourceobjectstringRequiredAlways
customeridintegerRequiredThe customer id.chatbot_idintegerRequiredThe chatbot the profile belongs to. Profiles are per chatbot: the same person on two chatbots is two customers.external_idstringnullableRequiredYour own identifier for the person, exactly asPOST /customerssent it;nullwhen none, or after anonymization. The strongest match key.full_namestringnullableRequiredName as captured or set;nullwhen unknown, or after anonymization.emailstringnullableRequiredEmail address as captured or sent (matching compares it lower-cased);nullwhen none, or after anonymization. Not verified.phonestringnullableRequiredPhone number as captured or sent (matching compares its E.164 form);nullwhen none, or after anonymization. Not verified.companystringnullableRequiredCompany name;nullwhen unknown, or after anonymization.lead_statusstringRequiredSales pipeline status:newwhen captured, then whatever you or the dashboard set (contacted,qualified,unqualified,converted). Anonymization setsblocked.Possible values
newcontactedqualifiedunqualifiedconvertedblockedlifecycle_stagestringRequiredRelationship stage:visitor(no contact yet),lead(a contact is known — where every new profile with one starts),customerorvip. Only a capture that brings a contact movesvisitortolead; the rest is yours to set.Possible values
visitorleadcustomervipsourcestringnullableRequiredWhere the profile was first captured:widget(the widget or the hosted agent page lead form),dashboard(Test Chat),action(acollect_infoCall Action),chat_intent(a visitor message that was only contact details) orapi(POST /customers). Never changes after creation.created_atstringnullableRequiredWhen the profile was created (ISO 8601, UTC).updated_atstringnullableRequiredWhen the profile last changed (ISO 8601, UTC).
- 400A tenant selector header named a tenant other than the API key's own (
ACCOUNT_SELECTOR_NOT_SUPPORTED): an all-digitX-Account-Idother than the key owner's id or, whileworkspaces.context_enabledis on, anX-Workspace-Idother than the key's workspace (ws_prefix optional, case-insensitive). A header naming the key's own tenant, or a non-numericX-Account-Id, is ignored; a key cannot switch tenants, so send neither. A mutation that honoursIdempotency-Keyalso returnsINVALID_IDEMPOTENCY_KEYwhen that header is not 16–128[A-Za-z0-9_-]characters.Error codesACCOUNT_SELECTOR_NOT_SUPPORTEDINVALID_IDEMPOTENCY_KEY
- 401Missing or invalid API key.
Error codesUNAUTHENTICATED
- 403The API key is missing a required scope.
Error codesACCOUNT_SUSPENDEDINSUFFICIENT_SCOPE
- 404The resource does not exist on this account.
Error codesNOT_FOUND
- 409The request conflicts with the current resource state.
Error codesIDEMPOTENCY_IN_FLIGHT
- 422Validation failed. See
error.details.Error codesIDEMPOTENCY_KEY_REUSEDVALIDATION_FAILED
- 429Rate limited. Honour the Retry-After header before retrying.
Error codesRATE_LIMITED
- 500Unexpected server error. Retry with the same Idempotency-Key.
Error codesSERVER_ERROR
Request
curl -X PATCH 'https://api.agentency.com/v1/customers/<CUSTOMER_ID>' \
-H "Authorization: Bearer <YOUR_KEY>" \
-H "Content-Type: application/json"
-d '{
"full_name": "full_name",
"company": "company",
"job_title": "job_title",
"website": "website",
"country": "country",
"lead_status": "lead_status",
"lifecycle_stage": "lifecycle_stage",
"extra": "extra"
}'Response
Example 200 response
{
"object": "customer",
"id": 1,
"chatbot_id": 1,
"external_id": "crm_2001",
"full_name": "Sam Rivera",
"email": "sam.rivera@example.com",
"phone": "+14155550123",
"company": "Contoso",
"lead_status": "contacted",
"lifecycle_stage": "customer",
"source": "api",
"created_at": "2026-01-01T00:00:00+00:00",
"updated_at": "2026-01-01T00:00:00+00:00"
}Delete a customer
/v1 and the dashboard and emits customer.deleted. Its conversations are kept, and the same email or phone captured later becomes a new customer. The profile's contact details are not erased: for a data-subject request use POST /customers/{id}/anonymize instead (or first).- Required scope
- customers:write
- Idempotency
- Accepts Idempotency-Key
- Webhook events
- customer.deleted
- Rate limits
Integer.id(path)Customer idPlaceholder in the samples:
<CUSTOMER_ID>Get it from
- GET /v1/customers returns it as
data[].id - POST /v1/customers returns it as
id - Webhook customer.created carries it as
data.object.id - Webhook lead.captured carries it as
data.object.id lead.capturednames the customer the lead was filed under: itsdata.objectis{"object": "lead", "id": <customer id>}. In the dashboard, the id is the number in a customer's address:/dashboard/customers/{id}.
- GET /v1/customers returns it as
Path parameters
idintegerRequiredThe customer id.
Header parameters
Idempotency-KeystringOptionalMakes a retry safe. The first 2xx answer is stored for 24 h and replayed for the same key withIdempotency-Replayed: true; a retry while the first request runs is 409IDEMPOTENCY_IN_FLIGHT; the same key with a different payload is 422IDEMPOTENCY_KEY_REUSED; a malformed key is 400INVALID_IDEMPOTENCY_KEY.Min length:
16Max length:128Pattern:^[A-Za-z0-9_-]+$
Responses
- 200Success.
objectstringRequiredAlways
customeridintegerRequiredThe deleted customer's id.deletedbooleanRequiredAlwaystrue.
- 400A tenant selector header named a tenant other than the API key's own (
ACCOUNT_SELECTOR_NOT_SUPPORTED): an all-digitX-Account-Idother than the key owner's id or, whileworkspaces.context_enabledis on, anX-Workspace-Idother than the key's workspace (ws_prefix optional, case-insensitive). A header naming the key's own tenant, or a non-numericX-Account-Id, is ignored; a key cannot switch tenants, so send neither. A mutation that honoursIdempotency-Keyalso returnsINVALID_IDEMPOTENCY_KEYwhen that header is not 16–128[A-Za-z0-9_-]characters.Error codesACCOUNT_SELECTOR_NOT_SUPPORTEDINVALID_IDEMPOTENCY_KEY
- 401Missing or invalid API key.
Error codesUNAUTHENTICATED
- 403The API key is missing a required scope.
Error codesACCOUNT_SUSPENDEDINSUFFICIENT_SCOPE
- 404The resource does not exist on this account.
Error codesNOT_FOUND
- 409The request conflicts with the current resource state.
Error codesIDEMPOTENCY_IN_FLIGHT
- 422Validation failed. See
error.details.Error codesIDEMPOTENCY_KEY_REUSED
- 429Rate limited. Honour the Retry-After header before retrying.
Error codesRATE_LIMITED
- 500Unexpected server error. Retry with the same Idempotency-Key.
Error codesSERVER_ERROR
Request
curl -X DELETE 'https://api.agentency.com/v1/customers/<CUSTOMER_ID>' \
-H "Authorization: Bearer <YOUR_KEY>" \
-H "Content-Type: application/json"Response
Example 200 response
{
"object": "customer",
"id": 1,
"deleted": true
}List a customer's timeline
created[gte] / created[lte] filter on occurred_at. Timeline entries are a record, not webhook events: subscribe to customer.* and lead.captured to be told as things happen.- Required scope
- customers:read
- Idempotency
- Read-only
- Rate limits
Integer.id(path)Customer idPlaceholder in the samples:
<CUSTOMER_ID>Get it from
- GET /v1/customers returns it as
data[].id - POST /v1/customers returns it as
id - Webhook customer.created carries it as
data.object.id - Webhook lead.captured carries it as
data.object.id lead.capturednames the customer the lead was filed under: itsdata.objectis{"object": "lead", "id": <customer id>}. In the dashboard, the id is the number in a customer's address:/dashboard/customers/{id}.
- GET /v1/customers returns it as
Path parameters
idintegerRequiredThe customer id.
Query parameters
limitintegerOptionalPage size, clamped to 1–100.Default:
20Minimum:1Maximum:100starting_afterstringOptionalCursor from a previous page'snext_cursor: returns the page after it. A cursor minted for another list,sortororderis 422INVALID_CURSOR.ending_beforestringOptionalCursor from a previous page'sprevious_cursor: returns the page before it. Wins when both cursors are sent.created[gte]stringOptionalOnly rows created on or after this instant:YYYY-MM-DD(whole UTC day) or an RFC 3339 timestamp with a zone. Anything else is 422INVALID_PARAMETER.created[lte]stringOptionalOnly rows created on or before this instant:YYYY-MM-DD(whole UTC day) or an RFC 3339 timestamp with a zone. Anything else is 422INVALID_PARAMETER.
Responses
- 200Success.
objectstringRequiredAlways
listdataarray of CustomerEventResourceRequiredhas_morebooleanRequirednext_cursorstringnullableRequiredprevious_cursorstringnullableRequiredurlstringRequired
- 400A tenant selector header named a tenant other than the API key's own (
ACCOUNT_SELECTOR_NOT_SUPPORTED): an all-digitX-Account-Idother than the key owner's id or, whileworkspaces.context_enabledis on, anX-Workspace-Idother than the key's workspace (ws_prefix optional, case-insensitive). A header naming the key's own tenant, or a non-numericX-Account-Id, is ignored; a key cannot switch tenants, so send neither.Error codesACCOUNT_SELECTOR_NOT_SUPPORTED
- 401Missing or invalid API key.
Error codesUNAUTHENTICATED
- 403The API key is missing a required scope.
Error codesACCOUNT_SUSPENDEDINSUFFICIENT_SCOPE
- 404The resource does not exist on this account.
Error codesNOT_FOUND
- 422Validation failed. See
error.details.Error codesINVALID_CURSORINVALID_PARAMETER
- 429Rate limited. Honour the Retry-After header before retrying.
Error codesRATE_LIMITED
- 500Unexpected server error. Retry with the same Idempotency-Key.
Error codesSERVER_ERROR
Request
curl -X GET 'https://api.agentency.com/v1/customers/<CUSTOMER_ID>/timeline' \
-H "Authorization: Bearer <YOUR_KEY>" \
-H "Content-Type: application/json"Response
Example 200 response
{
"object": "list",
"data": [
{
"object": "customer_event",
"id": 1,
"event_type": "lead_form_submitted",
"properties": {
"fields": [
"full_name",
"email",
"phone"
],
"source": "widget"
},
"chat_session_id": 1,
"occurred_at": "2026-01-01T00:00:00+00:00"
}
],
"has_more": false,
"next_cursor": null,
"previous_cursor": null,
"url": "/v1/customers/1/timeline"
}Export customers as CSV
GET /customers would list for the same filters as one CSV file, newest first, with no pagination. created[gte] / created[lte] and sort are not read here: bound the dates with date_from / date_to. Bulk personal data: it needs customers:write, and each export is audited (filter names only), with a customer_exported timeline entry when it is scoped to one chatbot.
A filtered set over the row cap (50,000) is 422 EXPORT_TOO_LARGE before any row is sent: narrow the filters, or page GET /customers instead.- Required scope
- customers:write
- Idempotency
- Read-only
- Rate limits
Integer.chatbot_id(query)Chatbot idPlaceholder in the samples:
<CHATBOT_ID>Get it from
- GET /v1/chatbots returns it as
data[].id - POST /v1/chatbots returns it as
id - GET /v1/chatbots/by_handle/{handle} returns it as
id - Webhook chatbot.created carries it as
data.object.id - In the dashboard, it is the number in a chatbot's address:
/dashboard/chatbots/{id}.
- GET /v1/chatbots returns it as
Query parameters
searchstringnullableOptionalText the customer's name, company, email or phone contains. At most 255 characters.Max length:
255chatbot_idintegernullableOptionalOnly this chatbot's customers. A chatbot the API key cannot reach matches nothing: the answer is an empty list, not 404.statusstringnullableOptionalOnly customers with thislead_status.Possible values
newcontactedqualifiedunqualifiedconvertedblockednulllifecycle_stagestringnullableOptionalOnly customers at thislifecycle_stage.Possible values
visitorleadcustomervipnullsourcestringnullableOptionalOnly customers first captured from thissource:widget,dashboard,action,chat_intentorapi. Exact match.Max length:
255utm_sourcestringnullableOptionalOnly customers whose captured visit carried thisutm_sourcetag. Exact match; only the widget and hosted agent page lead forms record UTM tags.Max length:
255date_fromstringnullableOptionalEarliest creation day, inclusive: Y-m-d or an RFC 3339 date-time (YYYY-MM-DDThh:mm:ss.ffffff). An instant counts as the UTC calendar day it falls on.Example:
"2026-10-01"Format:date-timedate_tostringnullableOptionalLatest creation day, inclusive: Y-m-d or an RFC 3339 date-time (YYYY-MM-DDThh:mm:ss.ffffff). An instant counts as the UTC calendar day it falls on.Example:
"2026-10-31T23:59:59Z"Format:date-time
Responses
- 200Success.
- 400A tenant selector header named a tenant other than the API key's own (
ACCOUNT_SELECTOR_NOT_SUPPORTED): an all-digitX-Account-Idother than the key owner's id or, whileworkspaces.context_enabledis on, anX-Workspace-Idother than the key's workspace (ws_prefix optional, case-insensitive). A header naming the key's own tenant, or a non-numericX-Account-Id, is ignored; a key cannot switch tenants, so send neither.Error codesACCOUNT_SELECTOR_NOT_SUPPORTED
- 401Missing or invalid API key.
Error codesUNAUTHENTICATED
- 403The API key is missing a required scope.
Error codesACCOUNT_SUSPENDEDINSUFFICIENT_SCOPE
- 422Validation failed. See
error.details.Error codesEXPORT_TOO_LARGEVALIDATION_FAILED
- 429Rate limited. Honour the Retry-After header before retrying.
Error codesRATE_LIMITED
- 500Unexpected server error. Retry with the same Idempotency-Key.
Error codesSERVER_ERROR
Request
curl -X GET 'https://api.agentency.com/v1/customers/export' \
-H "Authorization: Bearer <YOUR_KEY>" \
-H "Content-Type: application/json"Response
Example 400 response
{
"message": "string",
"error": {
"code": "ACCOUNT_SELECTOR_NOT_SUPPORTED",
"message": "string",
"details": {
"header": "X-Account-Id"
}
},
"request_id": "string"
}Built from the response schema, not recorded from a live call.
Anonymize a customer
external_id and every other personal field become null; its lead_status becomes blocked; the visitor's messages in its conversations read [redacted] and the replies are removed; and the personal data copied onto those conversations and their Call Action runs is cleared. The profile, its conversations, messages and timeline stay, so counts and analytics still add up, and the contact can never be matched again.
Returns the anonymized customer. It emits no webhook event; the timeline records customer_anonymized.- Required scope
- customers:write
- Idempotency
- Accepts Idempotency-Key
- Webhook events
- None
- Rate limits
Integer.id(path)Customer idPlaceholder in the samples:
<CUSTOMER_ID>Get it from
- GET /v1/customers returns it as
data[].id - POST /v1/customers returns it as
id - Webhook customer.created carries it as
data.object.id - Webhook lead.captured carries it as
data.object.id lead.capturednames the customer the lead was filed under: itsdata.objectis{"object": "lead", "id": <customer id>}. In the dashboard, the id is the number in a customer's address:/dashboard/customers/{id}.
- GET /v1/customers returns it as
Path parameters
idintegerRequiredThe customer id.
Header parameters
Idempotency-KeystringOptionalMakes a retry safe. The first 2xx answer is stored for 24 h and replayed for the same key withIdempotency-Replayed: true; a retry while the first request runs is 409IDEMPOTENCY_IN_FLIGHT; the same key with a different payload is 422IDEMPOTENCY_KEY_REUSED; a malformed key is 400INVALID_IDEMPOTENCY_KEY.Min length:
16Max length:128Pattern:^[A-Za-z0-9_-]+$
Responses
- 200
CustomerResourceobjectstringRequiredAlways
customeridintegerRequiredThe customer id.chatbot_idintegerRequiredThe chatbot the profile belongs to. Profiles are per chatbot: the same person on two chatbots is two customers.external_idstringnullableRequiredYour own identifier for the person, exactly asPOST /customerssent it;nullwhen none, or after anonymization. The strongest match key.full_namestringnullableRequiredName as captured or set;nullwhen unknown, or after anonymization.emailstringnullableRequiredEmail address as captured or sent (matching compares it lower-cased);nullwhen none, or after anonymization. Not verified.phonestringnullableRequiredPhone number as captured or sent (matching compares its E.164 form);nullwhen none, or after anonymization. Not verified.companystringnullableRequiredCompany name;nullwhen unknown, or after anonymization.lead_statusstringRequiredSales pipeline status:newwhen captured, then whatever you or the dashboard set (contacted,qualified,unqualified,converted). Anonymization setsblocked.Possible values
newcontactedqualifiedunqualifiedconvertedblockedlifecycle_stagestringRequiredRelationship stage:visitor(no contact yet),lead(a contact is known — where every new profile with one starts),customerorvip. Only a capture that brings a contact movesvisitortolead; the rest is yours to set.Possible values
visitorleadcustomervipsourcestringnullableRequiredWhere the profile was first captured:widget(the widget or the hosted agent page lead form),dashboard(Test Chat),action(acollect_infoCall Action),chat_intent(a visitor message that was only contact details) orapi(POST /customers). Never changes after creation.created_atstringnullableRequiredWhen the profile was created (ISO 8601, UTC).updated_atstringnullableRequiredWhen the profile last changed (ISO 8601, UTC).
- 400A tenant selector header named a tenant other than the API key's own (
ACCOUNT_SELECTOR_NOT_SUPPORTED): an all-digitX-Account-Idother than the key owner's id or, whileworkspaces.context_enabledis on, anX-Workspace-Idother than the key's workspace (ws_prefix optional, case-insensitive). A header naming the key's own tenant, or a non-numericX-Account-Id, is ignored; a key cannot switch tenants, so send neither. A mutation that honoursIdempotency-Keyalso returnsINVALID_IDEMPOTENCY_KEYwhen that header is not 16–128[A-Za-z0-9_-]characters.Error codesACCOUNT_SELECTOR_NOT_SUPPORTEDINVALID_IDEMPOTENCY_KEY
- 401Missing or invalid API key.
Error codesUNAUTHENTICATED
- 403The API key is missing a required scope.
Error codesACCOUNT_SUSPENDEDINSUFFICIENT_SCOPE
- 404The resource does not exist on this account.
Error codesNOT_FOUND
- 409The request conflicts with the current resource state.
Error codesIDEMPOTENCY_IN_FLIGHT
- 422Validation failed. See
error.details.Error codesIDEMPOTENCY_KEY_REUSED
- 429Rate limited. Honour the Retry-After header before retrying.
Error codesRATE_LIMITED
- 500Unexpected server error. Retry with the same Idempotency-Key.
Error codesSERVER_ERROR
Request
curl -X POST 'https://api.agentency.com/v1/customers/<CUSTOMER_ID>/anonymize' \
-H "Authorization: Bearer <YOUR_KEY>" \
-H "Content-Type: application/json"Response
Example 200 response
{
"object": "customer",
"id": 1,
"chatbot_id": 1,
"external_id": null,
"full_name": null,
"email": null,
"phone": null,
"company": null,
"lead_status": "blocked",
"lifecycle_stage": "lead",
"source": "widget",
"created_at": "2026-01-01T00:00:00+00:00",
"updated_at": "2026-01-01T00:00:00+00:00"
}Delete customers in bulk
DELETE /customers/{id} for up to 200 customers at once, in one transaction: each one deleted emits its own customer.deleted. Ids that do not exist or the API key cannot reach are skipped, so deleted can be lower than the number of ids sent.- Required scope
- customers:write
- Idempotency
- Accepts Idempotency-Key
- Webhook events
- customer.deleted
- Rate limits
Array of 1–200 integer customer ids. A repeated id counts once; an id the API key cannot reach is skipped, not an error.ids(body)Customer idsPlaceholder in the samples:
<CUSTOMER_IDS>Get it from
- GET /v1/customers returns it as
data[].id - POST /v1/customers returns it as
id - Webhook customer.created carries it as
data.object.id - Webhook lead.captured carries it as
data.object.id lead.capturednames the customer the lead was filed under: itsdata.objectis{"object": "lead", "id": <customer id>}. In the dashboard, the id is the number in a customer's address:/dashboard/customers/{id}.
- GET /v1/customers returns it as
Header parameters
Idempotency-KeystringOptionalMakes a retry safe. The first 2xx answer is stored for 24 h and replayed for the same key withIdempotency-Replayed: true; a retry while the first request runs is 409IDEMPOTENCY_IN_FLIGHT; the same key with a different payload is 422IDEMPOTENCY_KEY_REUSED; a malformed key is 400INVALID_IDEMPOTENCY_KEY.Min length:
16Max length:128Pattern:^[A-Za-z0-9_-]+$
Request body
idsarray of integerRequiredCustomer ids, 1–200 of them. An id that does not exist, is deleted, or the API key cannot reach is skipped rather than refused; the response counts only the customers acted on.Min items:
1Max items:200
Responses
- 200Success.
objectstringRequiredAlways
customer_bulkdeletedintegerRequiredHow many customers were deleted.Minimum:
0
- 400A tenant selector header named a tenant other than the API key's own (
ACCOUNT_SELECTOR_NOT_SUPPORTED): an all-digitX-Account-Idother than the key owner's id or, whileworkspaces.context_enabledis on, anX-Workspace-Idother than the key's workspace (ws_prefix optional, case-insensitive). A header naming the key's own tenant, or a non-numericX-Account-Id, is ignored; a key cannot switch tenants, so send neither. A mutation that honoursIdempotency-Keyalso returnsINVALID_IDEMPOTENCY_KEYwhen that header is not 16–128[A-Za-z0-9_-]characters.Error codesACCOUNT_SELECTOR_NOT_SUPPORTEDINVALID_IDEMPOTENCY_KEY
- 401Missing or invalid API key.
Error codesUNAUTHENTICATED
- 403The API key is missing a required scope.
Error codesACCOUNT_SUSPENDEDINSUFFICIENT_SCOPE
- 409The request conflicts with the current resource state.
Error codesIDEMPOTENCY_IN_FLIGHT
- 422Validation failed. See
error.details.Error codesIDEMPOTENCY_KEY_REUSEDVALIDATION_FAILED
- 429Rate limited. Honour the Retry-After header before retrying.
Error codesRATE_LIMITED
- 500Unexpected server error. Retry with the same Idempotency-Key.
Error codesSERVER_ERROR
Request
curl -X POST 'https://api.agentency.com/v1/customers/bulk_delete' \
-H "Authorization: Bearer <YOUR_KEY>" \
-H "Content-Type: application/json"
-d '{
"ids": []
}'Response
Example 200 response
{
"object": "customer_bulk",
"deleted": 1
}Anonymize customers in bulk
POST /customers/{id}/anonymize for up to 200 customers at once. Irreversible, and it emits no webhook event. Ids that do not exist or the API key cannot reach are skipped, so anonymized can be lower than the number of ids sent.- Required scope
- customers:write
- Idempotency
- Accepts Idempotency-Key
- Webhook events
- None
- Rate limits
Array of 1–200 integer customer ids. A repeated id counts once; an id the API key cannot reach is skipped, not an error.ids(body)Customer idsPlaceholder in the samples:
<CUSTOMER_IDS>Get it from
- GET /v1/customers returns it as
data[].id - POST /v1/customers returns it as
id - Webhook customer.created carries it as
data.object.id - Webhook lead.captured carries it as
data.object.id lead.capturednames the customer the lead was filed under: itsdata.objectis{"object": "lead", "id": <customer id>}. In the dashboard, the id is the number in a customer's address:/dashboard/customers/{id}.
- GET /v1/customers returns it as
Header parameters
Idempotency-KeystringOptionalMakes a retry safe. The first 2xx answer is stored for 24 h and replayed for the same key withIdempotency-Replayed: true; a retry while the first request runs is 409IDEMPOTENCY_IN_FLIGHT; the same key with a different payload is 422IDEMPOTENCY_KEY_REUSED; a malformed key is 400INVALID_IDEMPOTENCY_KEY.Min length:
16Max length:128Pattern:^[A-Za-z0-9_-]+$
Request body
idsarray of integerRequiredCustomer ids, 1–200 of them. An id that does not exist, is deleted, or the API key cannot reach is skipped rather than refused; the response counts only the customers acted on.Min items:
1Max items:200
Responses
- 200Success.
objectstringRequiredAlways
customer_bulkanonymizedintegerRequiredHow many customers were anonymized.Minimum:
0
- 400A tenant selector header named a tenant other than the API key's own (
ACCOUNT_SELECTOR_NOT_SUPPORTED): an all-digitX-Account-Idother than the key owner's id or, whileworkspaces.context_enabledis on, anX-Workspace-Idother than the key's workspace (ws_prefix optional, case-insensitive). A header naming the key's own tenant, or a non-numericX-Account-Id, is ignored; a key cannot switch tenants, so send neither. A mutation that honoursIdempotency-Keyalso returnsINVALID_IDEMPOTENCY_KEYwhen that header is not 16–128[A-Za-z0-9_-]characters.Error codesACCOUNT_SELECTOR_NOT_SUPPORTEDINVALID_IDEMPOTENCY_KEY
- 401Missing or invalid API key.
Error codesUNAUTHENTICATED
- 403The API key is missing a required scope.
Error codesACCOUNT_SUSPENDEDINSUFFICIENT_SCOPE
- 409The request conflicts with the current resource state.
Error codesIDEMPOTENCY_IN_FLIGHT
- 422Validation failed. See
error.details.Error codesIDEMPOTENCY_KEY_REUSEDVALIDATION_FAILED
- 429Rate limited. Honour the Retry-After header before retrying.
Error codesRATE_LIMITED
- 500Unexpected server error. Retry with the same Idempotency-Key.
Error codesSERVER_ERROR
Request
curl -X POST 'https://api.agentency.com/v1/customers/bulk_anonymize' \
-H "Authorization: Bearer <YOUR_KEY>" \
-H "Content-Type: application/json"
-d '{
"ids": []
}'Response
Example 200 response
{
"object": "customer_bulk",
"anonymized": 1
}