## Create a trace

`POST /v1/crm-traces`

Creates a sales-intelligence trace and binds it to CRM records. Call it after creating a lead or another record to save where the visitor came from.

## Request fields (body)

| Field | Type | Required | Description |
|------|-----|:-----:|---------|
| `trace` | object \| string | yes | A trace object or a string containing a JSON object. An array, a number, `null` and a string without a JSON object are rejected with `400 INVALID_PARAMS` |
| `entities` | array | no | Bindings to CRM records. Without the array or with an empty array, an unbound trace is created |
| `entities[].type` | string | yes | Record type: `LEAD`, `DEAL`, `CONTACT`, `COMPANY` or `QUOTE` |
| `entities[].id` | number \| string | yes | ID of an existing record — a positive integer; a string of digits is also accepted. List: [`GET /v1/leads`](/docs/entities/leads/list), [`GET /v1/deals`](/docs/entities/deals/list), [`GET /v1/contacts`](/docs/entities/contacts/list), [`GET /v1/companies`](/docs/entities/companies/list), [`GET /v1/quotes`](/docs/entities/quotes/list) |

A `trace` object is passed without renaming its keys; a string is passed as is. The site tracker `b24Tracker.guest.getTrace()` returns a ready-to-use string. Sales intelligence uses the following trace keys: `url`, `ref` — the address the visitor came from, `client` — the analytics client identifier, `gid`, `device.isMobile`, `pages.list` with `[url, timestamp, title]` triples, `tags.ts`, `tags.list` with UTM tags, `previous.list`, `channels` with the `code` and `value` fields. The object may be empty: `{"trace": {}}` creates a trace.

## Examples

### curl — personal key

```bash
curl -X POST "https://vibecode.bitrix24.com/v1/crm-traces" \
  -H "X-Api-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "trace": {
      "url": "https://example.com/form",
      "ref": "https://example.com/news",
      "tags": { "list": { "utm_source": "newsletter", "utm_campaign": "autumn" } }
    },
    "entities": [{ "type": "LEAD", "id": 123 }]
  }'
```

### curl — OAuth application

```bash
curl -X POST "https://vibecode.bitrix24.com/v1/crm-traces" \
  -H "X-Api-Key: YOUR_APP_KEY" \
  -H "Authorization: Bearer USER_SESSION_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "trace": {
      "url": "https://example.com/form",
      "ref": "https://example.com/news",
      "tags": { "list": { "utm_source": "newsletter", "utm_campaign": "autumn" } }
    },
    "entities": [{ "type": "LEAD", "id": 123 }]
  }'
```

### JavaScript — personal key

```javascript
const res = await fetch('https://vibecode.bitrix24.com/v1/crm-traces', {
  method: 'POST',
  headers: {
    'X-Api-Key': 'YOUR_API_KEY',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    trace: {
      url: 'https://example.com/form',
      ref: 'https://example.com/news',
      tags: { list: { utm_source: 'newsletter', utm_campaign: 'autumn' } },
    },
    entities: [{ type: 'LEAD', id: 123 }],
  }),
})

const { data } = await res.json()
console.log('Trace ID:', data.id)
```

### JavaScript — OAuth application

```javascript
const res = await fetch('https://vibecode.bitrix24.com/v1/crm-traces', {
  method: 'POST',
  headers: {
    'X-Api-Key': 'YOUR_APP_KEY',
    'Authorization': 'Bearer USER_SESSION_TOKEN',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    trace: {
      url: 'https://example.com/form',
      ref: 'https://example.com/news',
      tags: { list: { utm_source: 'newsletter', utm_campaign: 'autumn' } },
    },
    entities: [{ type: 'LEAD', id: 123 }],
  }),
})

const { data } = await res.json()
console.log('Trace ID:', data.id)
```

## Response fields

HTTP status `201 Created`.

| Field | Type | Description |
|------|-----|---------|
| `success` | boolean | Always `true` on success |
| `data` | object | The created trace |
| `data.id` | number | Trace identifier. Needed for [deletion](/docs/entities/crm-traces/delete) |

## Response example

```json
{
  "success": true,
  "data": {
    "id": 2025
  }
}
```

## Error response example

404 — a CRM record from `entities` was not found, the trace was not created:

```json
{
  "success": false,
  "error": {
    "code": "ENTITY_NOT_FOUND",
    "message": "Not found"
  }
}
```

## Errors

| HTTP | Code | Description |
|------|-----|----------|
| 400 | `MISSING_REQUIRED_FIELDS` | The body has no `trace`, or the body is not a JSON object |
| 400 | `INVALID_PARAMS` | `trace` is neither an object nor a string with a JSON object, the body root or an `entities` element has an extra key, `entities` is not an array, `entities[].type` is unknown, or `entities[].id` is not a positive integer |
| 404 | `ENTITY_NOT_FOUND` | A CRM record from `entities` was not found. The trace is not created |
| 403 | `BITRIX_ACCESS_DENIED` | No permission to read or update a CRM record from `entities` |
| 422 | `BITRIX_ERROR` | Bitrix24 rejected the trace creation. The error text is returned in `error.message` |
| 502 | `BITRIX_UNAVAILABLE` | Bitrix24 returned an invalid response to the record read or to the trace creation |
| 403 | `SCOPE_DENIED` | The key does not have the `crm` scope |
| 403 | `WRITE_BLOCKED_READONLY_KEY` | The key works in read-only mode |
| 401 | `TOKEN_MISSING` | The key has no configured tokens |

Full list of common API errors — [Errors](/docs/errors).

## Known specifics

**Records are checked before creation.** All records from `entities` are read before the trace is created; duplicate records are read only once. If at least one record is not found or not accessible, the trace is not created. Permission to update a record is checked when the trace is created. The check does not protect against a record being deleted between the read and the trace creation.

## See also

- [Delete a trace](/docs/entities/crm-traces/delete)
- [Sales-intelligence traces](/docs/entities/crm-traces)
- [Create a lead](/docs/entities/leads/create)
