> ## Documentation Index
> Fetch the complete documentation index at: https://docs.support.onclarity.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Customers and tickets

> Create and update customers, manage custom attributes, and change a ticket's status, assignee, category, and tags.

Use these endpoints to keep Clarity in sync with your CRM or order system, and to move tickets along from your own automations. Every request goes to `https://back.leap.onclarity.com` with `Authorization: Bearer $CLARITY_API_KEY` (see [Authentication](/authentication.md)).

## Customers

| Endpoint | Does |
| - | - |
| `POST /api/customer/create` | Create a customer |
| `POST /api/customer/edit` | Change a customer's fields |
| `GET /api/customer/list` | Search and filter customers |
| `GET /api/customer/{customer_id}` | Read one customer |
| `POST /api/customer/delete` | Delete a customer |
| `POST /api/customer/tags_set` | Replace a customer's tags |

### The customer object

```json theme={null}
{
  "id": 67890,
  "first_name": "Lena",
  "last_name": "Park",
  "avatar_url": null,
  "organization_id": 42,
  "attributes": [
    { "key": "external_id", "value": "crm-001928" },
    { "key": "personal_email", "value": "lena.park@example.com" },
    { "key": "personal_phone", "value": ["+14155550134"] }
  ],
  "tags": [
    { "id": 5, "name": "VIP", "description": null, "color": "#f5a623" }
  ],
  "custom_attributes": { "plan": "premium" },
  "gender": null,
  "gender_source": null,
  "merged_into_id": null,
  "has_open_suggestion": false,
  "channels": []
}
```

`attributes` is a list of `{key, value}` pairs. The standard keys are `external_id`, `personal_email`, and `personal_phone` (a list of numbers); Clarity adds others, such as first and last interaction times, as the customer writes in. `custom_attributes` holds the values of the [custom attributes](#custom-attributes) you define.

### Create a customer

```bash theme={null}
curl -X POST https://back.leap.onclarity.com/api/customer/create \
  -H "Authorization: Bearer $CLARITY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "first_name": "Lena",
    "last_name": "Park",
    "attributes": {
      "external_id": "crm-001928",
      "personal_email": "lena.park@example.com",
      "personal_phone": ["+14155550134"]
    },
    "tags": [5]
  }'
```

**first\_name** `string` *required*

The customer's first name.

**attributes** `object` *required*

Contact attributes as an object. Values are strings, `null`, or lists of strings. Send `{}` when you have none.

**last\_name** `string`

The customer's last name.

**avatar\_url** `string`

A URL of the customer's picture.

**tags** `integer[]`

Tag ids from `GET /api/tag/list`.

The response is the new customer object.

### Edit a customer

`POST /api/customer/edit` changes only the fields you send. Unknown fields are refused with `400`.

```bash theme={null}
curl -X POST https://back.leap.onclarity.com/api/customer/edit \
  -H "Authorization: Bearer $CLARITY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "id": 67890,
    "attributes": { "personal_email": "lena@example.org" },
    "whatsapp_phones": ["+14155550134"]
  }'
```

**id** `integer` *required*

The customer to edit.

**first\_name** `string`

New first name.

**last\_name** `string | null`

New last name.

**avatar\_url** `string | null`

New picture URL.

**attributes** `object`

Attributes to set. They are merged into the existing attributes; keys you leave out are kept.

**tags** `integer[]`

The customer's tags. Replaces the current list.

**gender** `string | null`

`male`, `female`, or `null`.

**whatsapp\_phones** `string[]`

The numbers on which the customer is reachable on WhatsApp. Replaces the current list.

The response is the updated customer object.

### List customers

```bash theme={null}
curl -G https://back.leap.onclarity.com/api/customer/list \
  -H "Authorization: Bearer $CLARITY_API_KEY" \
  --data-urlencode "search=lena" \
  --data-urlencode "custom_attributes=plan:premium" \
  --data-urlencode "sort_by=last_interaction:desc" \
  --data-urlencode "limit=50"
```

**search** `string`

Free-text search.

**channels** `string[]`

Customers who have talked on any of these channels: `web`, `android_sdk`, `ios_sdk`, `whatsapp`, `email`, `voice`, `x`, `instagram`, `linkedin`, `social`.

**tags** `integer[]`

Tag ids.

**custom\_attributes** `string[]`

Custom attribute filters as `key:value`. For numbers, `key:gte:10` and `key:lte:100` set a range. Repeat the parameter to combine filters.

**missing\_email** `boolean`

Only customers without an email.

**missing\_phone** `boolean`

Only customers without a phone number.

**last\_interaction\_days** `integer`

Only customers who interacted in the last N days.

**sort\_by** `string`

`name:asc`, `name:desc`, `last_interaction:asc`, or `last_interaction:desc`. The default is newest first.

**limit** `integer` (default: 50)

Page size.

**offset** `integer`

How many customers to skip.

The response is `{"list": [ ...customers ], "total": 1}`.

### Read, delete, and tag

* `GET /api/customer/{customer_id}` returns one customer. When the id belongs to a customer that was merged into another, you get the surviving customer and an `X-Contact-Merged-Into` header with its id. Replace the id you stored.
* `POST /api/customer/delete` with `{"id": 67890}` deletes the customer. An unknown id answers `404`.
* `POST /api/customer/tags_set` with `{"customer_id": 67890, "tag_ids": [5, 9]}` replaces the customer's tags.

## Custom attributes

Custom attributes are fields you define for tickets or for contacts (customers), such as a plan, an order number, or a contract date. Manage the definitions in **Settings > Custom attributes** or through the API.

| Endpoint | Does |
| - | - |
| `GET /api/custom-attributes/definitions/list?object_type=contact` | List definitions for `ticket` or `contact` |
| `POST /api/custom-attributes/definitions/create` | Add a definition |
| `POST /api/custom-attributes/definitions/edit` | Change a definition |
| `POST /api/custom-attributes/definitions/delete` | Delete a definition (`{"id": 31}`) |
| `POST /api/custom-attributes/definitions/reorder` | Move a definition `up` or `down` (`{"id": 31, "direction": "up"}`) |
| `GET /api/custom-attributes/values?object_type=ticket&object_id=12345` | Read a ticket's or customer's values |
| `POST /api/custom-attributes/values/set` | Set one value |
| `GET /api/custom-attributes/values/changes?object_type=ticket&object_id=12345` | Read the history of value changes |

### Define an attribute

```bash theme={null}
curl -X POST https://back.leap.onclarity.com/api/custom-attributes/definitions/create \
  -H "Authorization: Bearer $CLARITY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "object_type": "contact",
    "label": "Plan",
    "type": "list",
    "options": [
      { "id": "basic", "label": "Basic", "position": 0 },
      { "id": "premium", "label": "Premium", "position": 1 }
    ],
    "ai_context": true
  }'
```

**object\_type** `string` *required*

`ticket` or `contact`.

**label** `string` *required*

The name shown in Clarity, 1 to 100 characters. The attribute's `key` is generated from it and returned in the response.

**type** `string` *required*

`text`, `multiline`, `number`, `decimal`, `boolean`, `date`, `datetime`, or `list`.

**options** `object[]`

For `list`: the choices, each with `id`, `label`, and `position`.

**validation** `object`

Optional limits: `max_length`, `pattern` and `pattern_error_message`, `min` and `max`, `min_date` and `max_date`, `min_date_time` and `max_date_time`, `max_selections`.

**description** `string`

Help text, up to 65 characters.

**required** `boolean` (default: false)

Whether agents must fill it in.

**ai\_context** `boolean` (default: false)

Whether the AI agent sees the value.

**code\_context** `boolean` (default: false)

Whether the AI agent's code tools can read the value without it appearing in the prompt.

**hidden** `boolean` (default: false)

Keep the attribute off the customer and ticket pages; only admins see it.

### Set a value

```bash theme={null}
curl -X POST https://back.leap.onclarity.com/api/custom-attributes/values/set \
  -H "Authorization: Bearer $CLARITY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "object_type": "contact",
    "object_id": 67890,
    "key": "plan",
    "value": ["premium"]
  }'
```

The value must match the attribute's type:

| Type | Value |
| - | - |
| `text`, `multiline` | A string |
| `number` | A whole number |
| `decimal` | A number |
| `boolean` | `true` or `false` |
| `date`, `datetime` | A non-empty date string |
| `list` | A list of option ids |
| any | `null` clears the value |

The response is `{"values": {...}, "meta": {...}}` with all the object's current values.

## Update a ticket

Tickets are created by customers writing in, by the [AI service integration](/support/ai-service.md), and by [outbound messages](/support/messages.md). Once a ticket exists, these endpoints change it. Each one records an event in the ticket timeline and fires the matching [webhook](/support/webhooks.md).

### Look up ids first

| Endpoint | Returns |
| - | - |
| `GET /api/ticket_status/list` | `{"list": [...]}`: statuses with `id`, `name`, `state` (`open` or `closed`), `color`, `description` |
| `GET /api/ticket_priority/list` | A list of priorities with `id`, `name`, `color`, `weight` |
| `GET /api/ticket_category/list` | `{"items": [...], "total": N}`: categories with `id`, `label`, `parent_id` |
| `GET /api/tag/list?search=vip&limit=20` | `{"list": [...], "total": N}`: tags with `id`, `name`, `description`, `color` |
| `GET /api/team/list` | `{"list": [...], "total": N}`: teams with `id`, `name`, `status`, `member_count`, `assignment_method` |
| `GET /api/agent/list?limit=50&offset=0` | `{"list": [...], "total": N}`: agents with `id`, `first_name`, `email`, `role`, `kind` (`human` or `ai`), `teams` |

### Change the status

```bash theme={null}
curl -X POST https://back.leap.onclarity.com/api/ticket/status_set \
  -H "Authorization: Bearer $CLARITY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "ticket_id": 12345, "status_state": "closed" }'
```

Send exactly one of:

**status\_id** `integer`

A status id from `GET /api/ticket_status/list`.

**status\_state** `string`

`open` or `closed`. Clarity uses your organization's first status with that state.

The response is `{"created_at": "...", "internal_id": "..."}`. Moving a ticket to a closed status also fires the `ticket_closed` webhook.

### Assign a ticket

`POST /api/ticket/assign/v2` sets the team, the agent, or both. The body must contain both `team` and `agent` keys; set the one you do not want to change to `null`. To unassign, send `{"value": null}`.

```bash theme={null}
curl -X POST https://back.leap.onclarity.com/api/ticket/assign/v2 \
  -H "Authorization: Bearer $CLARITY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "ticket_id": 12345,
    "team": { "value": 7 },
    "agent": { "value": 311 }
  }'
```

| Body | Effect |
| - | - |
| `"team": {"value": 7}, "agent": null` | Assign to team 7 |
| `"team": null, "agent": {"value": 311}` | Assign to agent 311 |
| `"team": {"value": 7}, "agent": {"value": 311}` | Assign to team 7 and agent 311, who must be in that team |
| `"team": null, "agent": {"value": null}` | Remove the agent |

The response is `{"created_at": "...", "internal_id": {"team": "...", "agent": "..."}}`.

| Code | Status | Meaning |
| - | - | - |
| `assignment_not_found` | 404 | The team or agent does not exist, or the agent is not active. |
| `agent_not_in_the_team_conflict` | 409 | The agent is not a member of the team you named. |
| `agent_assign_conflict` | 409 | The ticket is assigned to a team the agent is not in. Send the team too. |

### Set the category

```bash theme={null}
curl -X POST https://back.leap.onclarity.com/api/ticket/category_set \
  -H "Authorization: Bearer $CLARITY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "ticket_id": 12345, "category_id": 18 }'
```

The response is `{"created_at": "...", "internal_id": "..."}`.

### Tag a ticket

**Replace all tags**

```bash theme={null}
curl -X POST https://back.leap.onclarity.com/api/ticket/tags_set \
  -H "Authorization: Bearer $CLARITY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "ticket_id": 12345, "tag_ids": [5, 9] }'
```

**Add or remove tags**

```bash theme={null}
curl -X POST https://back.leap.onclarity.com/api/ticket/tags_change \
  -H "Authorization: Bearer $CLARITY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "ticket_id": 12345, "add": [9], "remove": [5] }'
```

At least one of `add` and `remove` must name a tag.

Both answer `{}`.

> [!NOTE]
>
> Listing and searching tickets is not part of the documented Support API yet. To find a customer's tickets, use `GET /api/integration/ai_service/customer/{customer_id}/tickets` from the [AI service integration](/support/ai-service.md#read-the-customer), or keep the ticket ids you receive in [webhooks](/support/webhooks.md).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.