# 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.

Source: https://developer.kustomer.com/docs/concepts/json-api-format

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:

```json title="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.

```json title="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" }
  }
}
```

_An annotated, syntax-highlighted conversation response from GET /v1/conversations/{id}. Each part is outlined and explained: data, type, id, attributes (including custom and read-only fields such as messageCount), relationships to a customer and a createdBy user by type and id, and links.self. With ?include=customer, a top-level included array holds the customer, and selecting the customer relationship draws a connector to the included item with the same type and id. Toggles switch to the plain request body (flat attributes with no data or attributes wrapper, where name, status, priority and custom move into data.attributes in the response) and to a list response with meta, paging links and a data array._

## Lists

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

```json title="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](https://developer.kustomer.com/docs/concepts/pagination).

## Includes

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

```bash
curl "https://acme.api.kustomerapp.com/v1/customers/email=jane.doe%40example.com?include=company" \
  -H "Authorization: Bearer $KUSTOMER_API_KEY"
```

```json title="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.

```ts title="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:

```json title="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](https://developer.kustomer.com/docs/concepts/errors).

## Field conventions

| Convention                                          | Example                            |
| --------------------------------------------------- | ---------------------------------- |
| camelCase attribute names                           | `createdAt`, `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-only                 | `messageCount`, `updatedAt`, `rev` |
