Manage customers and identities through the API

Create and upsert customers, read and patch profiles, and link channel identities without confusing records or verification.

Updated

Use the public Platform API to associate your own customer records with a Protodesk workspace. These endpoints use https://api.protodesk.io/v1 and a server-held Bearer key. Check GET /v1/me before importing or changing records, and keep each key limited to customers:read, customers:write, or both as needed. A key is scoped to one workspace.

Choose identifiers before importing

Protodesk assigns an internal customer id for later resource URLs. If your system has a stable customer ID, pass it as externalId and keep that mapping; a display name or email can change. externalId must be unique within the workspace. Channel identities have a separate cid_… ID, a channel, and that channel's identifier. They are not interchangeable with the customer id or externalId.

POST /v1/customers creates a record. It requires at least one of externalId, name, email, phone, or company and returns the created customer with HTTP 201. PUT /v1/customers requires externalId and upserts the matching record, returning 201 when created or 200 when updated. Use upsert when your source system can provide a durable external ID; do not invent a new ID on every sync.

For example, this request upserts the same source customer on later syncs:

PUT /v1/customers
Authorization: Bearer <server-held key>
Content-Type: application/json

{"externalId":"crm-customer-123","name":"Example Customer","email":"customer@example.com"}

The example values are placeholders. Do not paste a real customer's details into a public test or documentation example. Where an endpoint supports Idempotency-Key, use a unique key for one logical operation and follow the retry guide; upsert is not a substitute for reconciling uncertain writes.

Read and update a customer

GET /v1/customers supports search and filters, including externalId. Keep the returned Protodesk id for GET /v1/customers/{customerId} and PATCH /v1/customers/{customerId}. Lists use an opaque nextCursor; do not construct one yourself. PATCH requires at least one changed field. The supported patch fields in the active handler are externalId, name, email, phone, company, language, timezone, and metadata. Check the public contract and validate against the deployed handler before relying on additional profile fields; some fields listed by the current schema are not accepted by this PATCH route.

DELETE /v1/customers/{customerId} marks the customer record deleted, rather than serving as a verified privacy-erasure or conversation-history deletion workflow. Do not promise downstream erasure from this endpoint. Confirm data-retention and privacy requirements through your approved process before using it.

Attach a channel identity only when you know who owns it

GET /v1/customers/{customerId}/identities lists that customer's identities. POST /v1/customers/{customerId}/identities requires channel and identifier; verified defaults to false. Set verified: true only if your system actually authenticated ownership of that channel identifier. A verified identity can affect automatic matching or merging. A claimed email by itself is not proof of ownership.

You can look up the linked customer with both identityChannel and identityIdentifier on GET /v1/customers. If the same channel and identifier already belong to another customer, creation returns a 409 identity conflict; inspect the owning customerId in the error rather than attaching it to a second record. DELETE /v1/customers/{customerId}/identities/{identityId} removes that identity link, not the customer or their past messages. Recheck ownership after any identity change.

For field definitions, responses, scopes, and current endpoint support, use the public OpenAPI contract. Do not use internal dashboard endpoints as integration APIs.

Related articles