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.

InteractiveLife of an API request
Your code. Builds the request: method, path, Authorization: Bearer <key>, and a JSON body with Content-Type: application/json.
POST /v1/customers · Authorization: Bearer eyJ…
Pick a scenario to see where it stops and what you get back. Select a stage to read what happens there.

Base URL

Send requests to your org's API host:

Text
https://<your-org>.api.kustomerapp.com

The 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

HeaderValueNotes
AuthorizationBearer <token>Required on every request. Note the space after Bearer.
Content-Typeapplication/jsonRequired when you send a body.
Acceptapplication/jsonOptional. Responses are always JSON.
User-Agentacme-sync/2.3 (+https://acme.example)Recommended. Identifies your integration when you contact support.

Methods

MethodUseTypical success
GETRead a resource or list200
POSTCreate a resource, run a search, start a bulk job201 (create), 200 (search)
PUTUpdate a resource200
PATCHUpdate specific attributes200
DELETEDelete a resource204 (most), 200 (some)

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.

put/v1/customers/{id}
cURL
# 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:

get/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:

Node.js
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

ParameterExampleEffect
page, pageSize?page=2&pageSize=100Page through lists. See Pagination.
include?include=companySide-load related resources into an included array. Supported values are listed per endpoint.
fields?fields=name,createdAtReturn only some attributes, where supported.
sort?sort=descSort direction or field, where supported.

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 importedAt when you load historical conversations, messages or custom objects. Records with importedAt skip the per-customer create rate limit, and you can backdate them with createdAt.
  • Author fields. createdAt, createdBy, modifiedAt and modifiedBy are normally set from the token. Overriding them needs the org.admin role.

Idempotency and retries

The REST API has no idempotency-key header. Design writes so they can be repeated safely:

  • Set externalId on everything you create, and look it up before creating (as above).
  • For bulk jobs, set _clientRef on each item to match results to inputs. See Bulk operations.
  • Retry 429 after the time in x-ratelimit-reset. Retry 5xx and network errors with exponential backoff and jitter.
  • Don't retry other 4xx responses: fix the request instead.
Node.js
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.