Making requests
Base URLs, headers, HTTP methods, update semantics, lookups by external ID, timestamps and retries for the Kustomer REST API.
The Kustomer API is a JSON-over-HTTPS REST API. Each resource has a URL, HTTP verbs act on it, and every request is authenticated with a bearer token.
Authorization: Bearer <key>, and a JSON body with Content-Type: application/json.Base URL
Send requests to your org's API host:
https://<your-org>.api.kustomerapp.comThe org-specific host always routes to the region your org lives in, so prefer it to the regional hosts. See Regions and base URLs for the US, EU and India hosts and when you need them. All requests must use HTTPS.
Headers
Methods
Update semantics
PUT and PATCH both merge by default: send only the attributes you want to change, and the rest stay as they are. Endpoints that support it accept ?replace=true to replace the whole resource instead.
/v1/customers/{id}
# Merge: only the tags change
curl -X PUT "https://acme.api.kustomerapp.com/v1/customers/$ID" \
-H "Authorization: Bearer $KUSTOMER_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "tags": ["vip", "beta-tester"] }'Looking up by your own IDs
Most objects accept an externalId, the ID from your system. Store it when you create the record, and you can fetch it later without keeping a Kustomer ID mapping. Lookups use an attribute=value path segment:
/v1/customers/externalId={externalId}
get/v1/customers/email={email}
get/v1/klasses/{name}/externalId={externalId}
URL-encode the value: jane+test@example.com becomes jane%2Btest%40example.com. A lookup that finds nothing returns 404, which makes a simple find-or-create pattern:
async function upsertCustomer(c: { externalId: string; name: string; email: string }) {
const found = await fetch(`${base}/v1/customers/externalId=${encodeURIComponent(c.externalId)}`, { headers })
if (found.status === 404) {
return fetch(`${base}/v1/customers`, {
method: 'POST',
headers,
body: JSON.stringify({ name: c.name, externalId: c.externalId, emails: [{ type: 'home', email: c.email }] }),
}).then((r) => r.json())
}
const { data } = await found.json()
return fetch(`${base}/v1/customers/${data.id}`, { method: 'PUT', headers, body: JSON.stringify({ name: c.name }) }).then((r) => r.json())
}Common query parameters
Each endpoint's reference page lists exactly which of these it accepts.
IDs and timestamps
- IDs are 24-character hexadecimal strings such as
5822a4a5aa5f92110005d69a. - Timestamps are ISO 8601 in UTC, for example
2026-10-21T14:02:11.925Z. Send them the same way. - Imported history. Set
importedAtwhen you load historical conversations, messages or custom objects. Records withimportedAtskip the per-customer create rate limit, and you can backdate them withcreatedAt. - Author fields.
createdAt,createdBy,modifiedAtandmodifiedByare normally set from the token. Overriding them needs theorg.adminrole.
Idempotency and retries
The REST API has no idempotency-key header. Design writes so they can be repeated safely:
- Set
externalIdon everything you create, and look it up before creating (as above). - For bulk jobs, set
_clientRefon each item to match results to inputs. See Bulk operations. - Retry
429after the time inx-ratelimit-reset. Retry5xxand network errors with exponential backoff and jitter. - Don't retry other
4xxresponses: fix the request instead.
async function kfetch(path: string, init: RequestInit = {}, attempt = 0): Promise<Response> {
const res = await fetch(`${base}${path}`, { ...init, headers: { ...headers, ...init.headers } })
if (res.status === 429 && attempt < 5) {
const reset = Number(res.headers.get('x-ratelimit-reset')) * 1000
await sleep(Math.max(reset - Date.now(), 1000))
return kfetch(path, init, attempt + 1)
}
if (res.status >= 500 && attempt < 5) {
await sleep(2 ** attempt * 500 + Math.random() * 250)
return kfetch(path, init, attempt + 1)
}
return res
}
const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms))Try it from the reference
Every operation in the API reference has a Try it panel. Paste a key, pick your region, and send a real request from your browser. Keys stay in memory and are never stored. Write requests ask you to confirm first.
Use Postman
Download the Postman collection and import it into Postman. It has every operation in the reference, grouped by API, with example parameters and bodies. Set the orgName and apiKey collection variables, and requests go to https://{orgName}.api.kustomerapp.com with your key as a bearer token. To use a regional host instead, change baseUrl. Optional query parameters and headers are included but turned off. Each request's description lists the roles it requires and links to its reference page.