Request and response format

The JSON shapes the Kustomer API accepts and returns, covering request bodies, the data envelope, relationships, side-loaded includes, lists and errors.

The Kustomer API uses a JSON:API-style envelope for responses and plain JSON for request bodies. Once you know the four shapes on this page, you can read any response in the reference.

Requests: plain attributes

Request bodies are flat JSON objects of attributes. Relationships are set with an ID:

POST /v1/customers/{id}/conversations
{
  "name": "Where is my order?",
  "status": "open",
  "priority": 3,
  "assignedTeams": ["5cb3a1b7c9e77c0013e5f2d1"],
  "custom": { "orderNumberStr": "4417" }
}

Don't wrap request bodies in data or attributes. Send Content-Type: application/json.

Single resources

A single resource comes back in data, with:

  • type: the resource type, such as customer, conversation or kobject;
  • id: Kustomer's ID;
  • attributes: the fields, including custom;
  • relationships: links to related resources by type and ID;
  • links.self: the resource's own path.
200 OK
{
  "data": {
    "type": "conversation",
    "id": "58228e05d4e96711004eccb7",
    "attributes": {
      "name": "Where is my order?",
      "status": "open",
      "priority": 3,
      "messageCount": 2,
      "custom": { "orderNumberStr": "4417" },
      "createdAt": "2026-10-21T14:02:11.925Z",
      "updatedAt": "2026-10-21T14:05:40.118Z"
    },
    "relationships": {
      "customer": {
        "links": { "self": "/v1/customers/5822a4a5aa5f92110005d69a" },
        "data": { "type": "customer", "id": "5822a4a5aa5f92110005d69a" }
      },
      "createdBy": {
        "links": { "self": "/v1/users/58190b991f2932100010d683" },
        "data": { "type": "user", "id": "58190b991f2932100010d683" }
      }
    },
    "links": { "self": "/v1/conversations/58228e05d4e96711004eccb7" }
  }
}

Lists

Lists return an array in data, plus meta and links for paging:

200 OK
{
  "meta": { "pageSize": 100, "page": 1 },
  "links": {
    "self": "/v1/brands?page=1&pageSize=100",
    "first": "/v1/brands?page=1&pageSize=100",
    "prev": null,
    "next": null
  },
  "data": [
    { "type": "brand", "id": "5daf7921124be4f2960cb196", "attributes": { "name": "default", "default": true } }
  ]
}

Some lists add total and totalPages to meta. See Pagination.

Includes

Endpoints that support include can side-load related resources into a top-level included array, saving extra requests:

Shell
curl "https://acme.api.kustomerapp.com/v1/customers/email=jane.doe%40example.com?include=company" \
  -H "Authorization: Bearer $KUSTOMER_API_KEY"
200 OK (excerpt)
{
  "data": {
    "type": "customer",
    "id": "5822a4a5aa5f92110005d69a",
    "relationships": {
      "company": { "data": { "type": "company", "id": "5bd7b2f6f1d4b10013e8a7c2" } }
    }
  },
  "included": [
    { "type": "company", "id": "5bd7b2f6f1d4b10013e8a7c2", "attributes": { "name": "Acme Corp" } }
  ]
}

Match items in included to relationships by type and id. Each endpoint's reference page lists the values its include parameter supports.

Node.js
function resolve(doc: { included?: Resource[] }, rel?: { data?: { type: string; id: string } }) {
  return rel?.data && doc.included?.find((r) => r.type === rel.data!.type && r.id === rel.data!.id)
}

const company = resolve(body, body.data.relationships.company)

Errors

Errors return an errors array:

400 Bad Request
{
  "errors": [
    { "code": "bad_request", "message": "Invalid request body or parameters.", "meta": {} }
  ]
}

A few endpoints use the fuller JSON:API error object, with status, title, detail and source. Parse defensively: read code, then message, then detail, then title. See Errors.

Field conventions

ConventionExample
camelCase attribute namescreatedAt, assignedUsers
ISO 8601 UTC timestamps"2026-10-21T14:02:11.925Z"
24-character hexadecimal IDs"5822a4a5aa5f92110005d69a"
Custom attributes under custom with a type suffix"custom": { "tierStr": "gold" }
Server-managed fields are read-onlymessageCount, updatedAt, rev