Bulk create customers

post/v1/customers/bulk
Requires any one of these rolesAbout roles
org.permission.customer.create
Legacyorg.user.customer.write

The API key or user must hold at least one of them. Legacy roles still work; prefer the others for new keys.

Creates customers in bulk.

_clientRef Correlation (Optional)

You can include a _clientRef string on each request item to correlate results back to your original entries without having to match on objectId order.

Rules

  • Optional on the request as a whole — you may omit _clientRef entirely and use the standard objectIds approach instead.
  • All-or-nothing when used: if any item includes _clientRef, every item must. Sending a mixed payload — some items with _clientRef, some without — returns a 400 error.
  • _clientRef must be a non-empty string, max 255 characters, and unique within the request array.

Async Poll Flow

  1. POST /v1/customers/bulk — submit the array with _clientRef on each item. Response returns a bulk operation ID.
  2. GET /v1/bulk/:id — poll until status is complete.
  3. GET /v1/bulk/:id/batches — retrieve the results[] array for per-item outcomes.

results[] Shape in Batch Response

When _clientRef was supplied, each batch object includes a results array:

[
  { "_clientRef": "crm-001", "objectId": "507f1f77bcf86cd799439031", "status": "created" },
  { "_clientRef": "crm-002", "status": "error", "error": "duplicate detected" }
]
  • status: "created", "updated", or "error"
  • objectId: ID of the created or updated object — omitted on error entries
  • error: failure reason — present only when status is "error"

The same results[] shape applies to array-shaped bulk updates (PUT /v1/customers/bulk with a request body array); status will be "updated" for successful rows.

Limitations

  • _clientRef is not supported on bulk update via ?ids= query param mode (planned for a future release)
  • _clientRef is not supported on bulk delete (planned for a future release)
About this resource

Customers

A customer is one person your team supports. Every conversation, message, note and custom object about that person hangs off their customer record, and agents see it all on a single timeline.

Kustomer is organized around the customer rather than the ticket. When someone emails, chats or calls for the first time, Kustomer creates a customer for them and matches later contact on the same email address, phone number or social handle back to that record. In the agent desktop the customer is the profile and the timeline of everything that has happened with them: conversations, orders and other custom objects, and tracked events. You can also create customers yourself, usually to sync people from your own database or CRM before they ever contact support.

Identifiers and matching

A customer carries several kinds of contact identifiers, each a list of objects with a type and an optional verified flag: emails, phones, whatsapps, socials and externalIds. Values in these lists must be unique across your org, because Kustomer uses them to route new inbound messages to the right person; a write that would give a second customer the same email, phone or external ID is rejected. When people genuinely share a contact point, such as a family email address, use sharedEmails, sharedPhones, sharedSocials or sharedExternalIds, which have no uniqueness rule.

externalId is a shortcut for your own ID for the person. Set it on create and you can look the customer up later by it, which makes syncs idempotent. You can also look customers up by email or phone.

What the record holds

Besides name, contact identifiers, locations, locale, timeZone and tags, a customer has a company (the company they belong to) and custom attributes under custom. Kustomer maintains several read-only summaries for you: conversationCounts (open, snoozed, done and all), lastConversation, lastMessageIn, lastActivityAt and preview, a snippet of the latest message.

Merging duplicates

Duplicates happen, for example when the same person writes from a work and a personal email. A merge combines a source customer into a target. Conversations, messages, notes and custom objects move to the target; contact identifiers, tags and custom attributes are combined (the target wins when both have the same custom attribute); and the source is deleted. A merge reports status as pending, running, success or error. Merging a customer into itself, or starting a merge while another one for the same pair is in progress, fails with 409 Conflict. There is no unmerge endpoint.

Which endpoint to use

To Call
Create, read, update or delete one customer POST /v1/customers, GET, PUT, PATCH or DELETE /v1/customers/{id}
Find a customer by your own ID GET /v1/customers/externalId={externalId}
Find a customer by contact details GET /v1/customers/email={email} or GET /v1/customers/phone={phone}
Page through customers by created or updated date GET /v1/customers
Import, update or delete up to 100 customers per request POST, PUT or DELETE /v1/customers/bulk
Merge a duplicate into another customer POST /v1/customers/merges
Check a merge, or list a customer's merges GET /v1/customers/merges/{id} or GET /v1/customers/{id}/merges

PUT merges the attributes you send into the record unless you add replace=true, but leaving out company unlinks the customer from their company, so always resend it. PATCH also accepts a JSON Patch body (Content-Type: application/json-patch+json), which is handy for appending one email or phone without resending the list; pass rev to make the write depend on the revision you last read. Customer updates are subject to object limits. GET /v1/customers only filters on createdAt and updatedAt; use search for anything richer. Bulk requests run asynchronously, as described in Bulk operations.

Request body

application/jsonrequired

array<object>

namestringnullable
max length 256
companystringnullable
externalIdstringnullable
min length 1 · max length 256
usernamestringnullable
max length 256
signedUpAtstring<date-time>nullable
lastActivityAtstring<date-time>nullable
lastCustomerActivityAtstring<date-time>nullable
lastSeenAtstring<date-time>nullable
avatarUrlstring<uri>nullable
array<external_id>
max items 10 · unique items
array<external_id>
max items 10 · unique items
array<email>
max items 10 · unique items
array<email>
max items 10 · unique items
array<phone>
max items 10 · unique items
array<phone>
max items 10 · unique items
array<whatsapp>
max items 10 · unique items
array<facebook_id>
max items 10 · unique items
array<instagram_id>
max items 10 · unique items
array<social>
max items 10 · unique items
array<social>
max items 10 · unique items
array<url>
max items 10 · unique items
array<location>
max items 10 · unique items
localestringnullable
max length 16
timeZonestringnullable
max length 64
tagsarray<tag_name>
max items 20 · unique items
objectsentiment
customobjectcustom_attrs
birthdayAtstring<date-time>nullable
genderstringnullable
One of"m""f"
createdAtstring<date-time>
importedAtstring<date-time>
revnumber
defaultLangstringlangnullable
max length 100
_clientRefstringclient_ref
min length 1 · max length 255

Responses

Created

objectrequired