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

# Conversions

> List conversion events with the source of the visit each one happened in, and window totals for paging.

<Warning>
  **Dashboard session only.** This endpoint authenticates with a Clerk session, not with a
  `tk_live_` API key. A key gets `401` here, and there is no permission you can add that changes
  that. It is documented because it exists and the dashboard uses it, not because you can call it
  with a key today.

  For the same numbers with a key, use [`GET /v1/goals/timeseries`](/api/goals#get-v1goalstimeseries)
  for goal completions over time, or [`GET /v1/sessions`](/api/sessions) for the sessions behind
  them.
</Warning>

## `GET /v1/conversions`

One page of conversion events recorded by `traceten.track()` or a server SDK, newest first, one row
per conversion. Each row carries two separate answers about where it came from:

* **The visit's source** (`source_kind`, `source_name`, `session_ai_source`): where the visit the
  conversion happened in came from. This is what the dashboard's Conversions page shows.
* **The model's credit** (`ai_source`, `confidence`): the source credited under the attribution
  model you ask for.

`site_id` here is the site's **Postgres id**, the UUID in your dashboard URL. That is different from
the key-authenticated read endpoints, which take the `ttid_` snippet key. The difference follows the
credential: this route only ever sees a dashboard session.

```bash theme={null}
curl "https://api.traceten.com/v1/conversions?site_id=018f0000-0000-7000-8000-000000000000&from=2026-08-01&to=2026-08-07&page_size=50"
```

### Query parameters

| Parameter           | Type    | Required | Notes                                                                                                |
| ------------------- | ------- | -------- | ---------------------------------------------------------------------------------------------------- |
| `site_id`           | string  | yes      | `sites.id`, a UUID. Not the `ttid_` snippet key.                                                     |
| `from`              | string  | no       | `YYYY-MM-DD`, inclusive. Defaults to 7 days ago.                                                     |
| `to`                | string  | no       | `YYYY-MM-DD`, inclusive. Defaults to today.                                                          |
| `event_name`        | string  | no       | One event name, 1 to 64 characters.                                                                  |
| `source`            | string  | no       | Only conversions credited to this source under `attribution_model`, compared without regard to case. |
| `attribution_model` | string  | no       | `first_touch`, `last_touch`, `linear` or `time_decay`. Defaults to `last_touch`.                     |
| `page`              | integer | no       | 1-based. Defaults to 1.                                                                              |
| `page_size`         | integer | no       | 1 to 200. Defaults to 50.                                                                            |

Unknown parameters are rejected with `422` rather than ignored, so a filter this endpoint cannot
apply never returns a `200` answering a different question.

`(page - 1) * page_size` may not exceed 100000. ClickHouse reads the rows an `OFFSET` skips, so a
very deep page is the most expensive request this endpoint can serve; past that ceiling it is a
`422`.

### Response

```json theme={null}
{
  "conversions": [
    {
      "event_id": "0f3c7a1e-9b21-4a77-9d0e-7c1a2b3c4d5e",
      "timestamp": "2026-08-04T14:22:07Z",
      "event_name": "purchase",
      "ai_source": null,
      "confidence": null,
      "visitor_id_short": "9f2a41bc",
      "value_cents": 4900,
      "currency": "GBP",
      "source_kind": "non_ai",
      "source_name": "google.com",
      "session_ai_source": "search",
      "country_code": "DE",
      "display_value": 57.33,
      "properties": { "plan": "annual", "items": 2 }
    }
  ],
  "page": 1,
  "page_size": 50,
  "has_more": false,
  "total_count": 1,
  "event_names": [{ "name": "purchase", "count": 1 }],
  "display_currency": "EUR"
}
```

Each row:

| Field               | Type           | Notes                                                                                                                                                                |
| ------------------- | -------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `event_id`          | string         | The conversion event's id.                                                                                                                                           |
| `timestamp`         | string         | When the event was recorded, UTC.                                                                                                                                    |
| `event_name`        | string         | The event name that was sent.                                                                                                                                        |
| `source_kind`       | string         | The visit's category: `ai`, `non_ai` or `unknown`. Empty when no visit matched the conversion.                                                                       |
| `source_name`       | string         | The host the visit came from, such as `google.com` or `chatgpt.com`. Empty when none was recorded.                                                                   |
| `session_ai_source` | string         | The visit's source label, such as `ChatGPT`, `search` or `direct`. Empty when no visit matched.                                                                      |
| `country_code`      | string         | The visit's country. Empty when unknown.                                                                                                                             |
| `ai_source`         | string \| null | The source credited under `attribution_model`. `null` when that model credited none.                                                                                 |
| `confidence`        | number \| null | That source's credit fraction, between 0 and 1. `null` when `ai_source` is.                                                                                          |
| `visitor_id_short`  | string         | First 8 characters of the visitor id. A display token, not a full identifier.                                                                                        |
| `value_cents`       | integer        | The value in whole minor units of `currency`. 0 when the event was sent without a value.                                                                             |
| `currency`          | string         | ISO 4217 code the value was recorded in.                                                                                                                             |
| `display_value`     | number \| null | The value in `display_currency`, in major units. `null` when there is no value or no exchange rate for that day.                                                     |
| `properties`        | object \| null | The parameters the event was sent with, after personal data is removed at the edge. `{}` when it had none, `null` when the stored object is larger than 8,192 bytes. |

Under `linear` and `time_decay` a conversion is split across the visits that led to it. It still
appears once: `ai_source` is the source with the largest share and `confidence` is that share.

Top level:

| Field              | Type            | Notes                                                                                                                              |
| ------------------ | --------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
| `page`             | integer         | The page you asked for.                                                                                                            |
| `page_size`        | integer         | The page size you asked for.                                                                                                       |
| `has_more`         | boolean         | `true` whenever a full page came back, so the last page can report `true` and the next come back empty.                            |
| `total_count`      | integer \| null | Conversions in the window, for `event_name` when given, from daily totals. `null` with `source`, or when the count cannot be read. |
| `event_names`      | array           | Each event name in the window with its `count`, most first, at most 100.                                                           |
| `display_currency` | string          | The site's display currency.                                                                                                       |

`display_value` uses the European Central Bank reference rate for the day of the conversion, or the
most recent one published before it. `total_count` comes from daily totals, so a conversion removed
with the [data deletion API](/privacy/data-deletion) can still be counted after its row is gone.

### Errors

| Status | Meaning                                                                         |
| ------ | ------------------------------------------------------------------------------- |
| `401`  | No dashboard session. An API key produces this.                                 |
| `403`  | `{ "error": "Forbidden", "statusCode": 403 }`. The site is not on this account. |
| `404`  | No account for the credential.                                                  |
| `422`  | Bad or unknown query parameter, or a page past the offset ceiling.              |
| `429`  | Rate limited. Back off and retry.                                               |
| `500`  | `{ "error": "Failed to load conversions" }`. Note there is no `statusCode`.     |

`401`, `403` and `404` all carry `{ "error": …, "statusCode": … }`.

## Next

* [Goals API](/api/goals) for the goal registry and completions over time.
* [Sessions API](/api/sessions) for the sessions these conversions belong to.
