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

# API reference

> Every endpoint, parameter, and limit of the Datalyr API at api.datalyr.com/v1.

The Datalyr API hands you one workspace's analytics, attribution, revenue, and workspace
data as JSON. Most endpoints read, and a read changes nothing. A small, scoped set of
endpoints changes conversion rules and trackable links, or drafts a container script.

```text theme={null}
https://api.datalyr.com/v1
```

Every `/v1` endpoint needs an Agent key, with one exception: `POST /signup` needs no
credential at all. See [Authentication](/api-reference/authentication). Each endpoint that
changes something needs a named key carrying that endpoint's scope. See
[Agent access](/api-reference/agents).

## Read endpoints

Each endpoint has its own page in this section, with the full parameter list and the
response schema. `None` in the last column means the endpoint takes no required
parameter.

| Method | Path                              | Returns                                                                                                            | Required                                                |
| ------ | --------------------------------- | ------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------- |
| `GET`  | `/overview`                       | `visitors_total`, `pageviews_total`, `sessions_total`, `custom_events_total`, `custom_event_counts`, `time_series` | `start_date`, `end_date`                                |
| `GET`  | `/traffic`                        | Referrer, campaign, or page rows with `name`, `count`, `percentage`                                                | `start_date`, `end_date`                                |
| `GET`  | `/devices`                        | Browser, OS, or device-type rows with `name`, `count`, `percentage`                                                | `start_date`, `end_date`                                |
| `GET`  | `/locations`                      | Country, region, or city rows with `name`, `count`, `percentage`                                                   | `start_date`, `end_date`                                |
| `GET`  | `/realtime`                       | `active_visitors`, `pageviews`, `events`, `revenue`, up to 50 `visitors`, `timeline`                               | None                                                    |
| `GET`  | `/events`                         | Raw event rows, `total_count`, `has_more`, `limit`, `offset`                                                       | `start_date`, `end_date`                                |
| `GET`  | `/events/properties`              | Custom `event_data` keys per event name, each with a count, a distinct-value count, a sample, and a type           | None                                                    |
| `GET`  | `/event-names`                    | Distinct event names of the last 30 days, plus `integrations`                                                      | None                                                    |
| `GET`  | `/users`                          | User rows, `total_count`, `has_more`, `limit`, `offset`                                                            | `start_date`, `end_date`                                |
| `GET`  | `/users/{userId}`                 | `user`, `sessions`, `conversions`                                                                                  | `userId` in the path                                    |
| `GET`  | `/retention`                      | Person-grain cohorts, one row per elapsed period, with active persons, retention rate, and revenue                 | None                                                    |
| `GET`  | `/ltv`                            | Cohort revenue per period, cumulative revenue, and cumulative revenue per acquired person                          | None                                                    |
| `GET`  | `/funnel`                         | Ordered funnel over 2 to 5 event names, with per-step conversion rates                                             | `steps`                                                 |
| `GET`  | `/attribution`                    | `campaigns` grouped by UTM, with visitors, conversions, revenue, rates                                             | `start_date`, `end_date`                                |
| `GET`  | `/ads`                            | Provider-reported spend, delivery, conversions, and ROAS per ad entity                                             | `start_date`, `end_date` or `date_from`, `date_through` |
| `GET`  | `/revenue`                        | Per-platform and combined revenue, orders, subscriptions, refunds, AOV                                             | `start_date`, `end_date`                                |
| `GET`  | `/commerce-metrics`               | Policy-governed revenue, spend, profit, margin, ROAS, MER, completeness                                            | `start_date`, `end_date`                                |
| `POST` | `/metrics`                        | Up to 50 catalog metrics over one range, with an optional preceding window                                         | A JSON body                                             |
| `GET`  | `/metrics/catalog`                | Every metric id `POST /metrics` accepts, with unit, category, and availability                                     | None                                                    |
| `GET`  | `/tags`                           | Tracking-link tag rows with `tag`, `count`, `percentage`, `source`                                                 | `start_date`, `end_date`                                |
| `GET`  | `/workspace`                      | `id`, `name`, `domain`, `timezone`, `connected_platforms`                                                          | None                                                    |
| `GET`  | `/usage`                          | `events_used`, `events_limit`, `postbacks_used`, `postbacks_limit`, `period_start`, `period_end`, `plan`           | None                                                    |
| `GET`  | `/onboarding`                     | What is set up in this workspace, and the `next_steps` a person still has to take                                  | None                                                    |
| `GET`  | `/docs/search`                    | Matching sections of this documentation, each with a link and an excerpt                                           | `q`                                                     |
| `GET`  | `/exports`                        | Recent export jobs and their status, newest first. No download URLs                                                | None                                                    |
| `GET`  | `/exports/{exportId}`             | One job's status, plus a signed `download` URL once the file exists                                                | `exportId` in the path                                  |
| `GET`  | `/rules`                          | Every conversion rule in the workspace                                                                             | None                                                    |
| `GET`  | `/links`                          | Every trackable link in the workspace                                                                              | None                                                    |
| `GET`  | `/scripts/proposals`              | Container-script drafts, newest first, with the verdict a human reached                                            | None                                                    |
| `GET`  | `/scripts/proposals/{proposalId}` | One draft as submitted, plus the reviewer's note                                                                   | `proposalId` in the path                                |
| `GET`  | `/audit`                          | Applied writes, newest first, each carrying the state it replaced                                                  | None                                                    |

`POST /metrics` is a read that takes a body, because 50 metric ids do not belong in a
query string.

## Endpoints that change something

These need a named key carrying the scope in the last column. The original per-workspace
Agent key resolves to `datalyr:read` alone, so it reads every route above and changes
none of these.

| Method   | Path                    | Does                                                  | Scope                                 |
| -------- | ----------------------- | ----------------------------------------------------- | ------------------------------------- |
| `POST`   | `/rules`                | Creates a conversion rule                             | `datalyr:write:rules`                 |
| `PATCH`  | `/rules/{ruleId}`       | Changes the fields you send, and no others            | `datalyr:write:rules`                 |
| `DELETE` | `/rules/{ruleId}`       | Deletes a conversion rule                             | `datalyr:write:rules`                 |
| `POST`   | `/links`                | Creates a trackable link                              | `datalyr:write:links`                 |
| `PATCH`  | `/links/{linkId}`       | Changes a link's name, destination, or UTM parameters | `datalyr:write:links`                 |
| `DELETE` | `/links/{linkId}`       | Deletes a trackable link                              | `datalyr:write:links`                 |
| `POST`   | `/scripts/proposals`    | Records a container-script draft. It installs nothing | `datalyr:propose:scripts`             |
| `POST`   | `/exports`              | Starts an export job. It returns a job id, never data | `datalyr:read`                        |
| `POST`   | `/audit/{auditId}/undo` | Restores the state one audit entry replaced           | The scope of the write being reversed |

Four rules govern every one of them.

| Rule              | Detail                                                                                                                                                                                                          |
| ----------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `dry_run`         | Every one accepts it. Over raw HTTP it defaults to `false`, because a `DELETE` you sent is a `DELETE` you meant. Send `dry_run: true` to read the exact change first. The MCP write tools default it to `true`. |
| `Idempotency-Key` | `POST` accepts this header. An identical retry inside 24 hours returns the first response with `Idempotent-Replay: true`, rather than a second object.                                                          |
| Audit             | Every applied write records what changed and the state it replaced, whether it came from this API or the dashboard. Read `GET /audit`.                                                                          |
| Undo              | `POST /audit/{auditId}/undo` puts the resource back. It refuses rather than guesses: it will not reverse a create, run twice, or restore over a resource that has since been deleted.                           |

A container script is arbitrary JavaScript on every page of your site. That is why
`POST /scripts/proposals` only drafts one: no scope publishes a script, and no endpoint
approves one. An owner or admin approves it in the dashboard.

## Signup takes no credential

`POST /signup` is the only route here that needs no key. It sends one verification email
and returns `{"status": "verification_sent"}` for a new address and an existing one
alike. Nothing is created until the recipient opens the link. See
[Agent-driven setup](/developer/agent-driven-setup).

## Date parameters

Most reporting endpoints take a date range. `/realtime`, `/event-names`,
`/users/{userId}`, `/workspace`, and `/usage` take none. `/retention`, `/ltv`, and
`/funnel` accept a range and fall back to a default when you omit it, so read each
endpoint's own page for its rule. The table below covers the endpoints that require both
dates.

| Parameter    | Rule                                  |
| ------------ | ------------------------------------- |
| `start_date` | Inclusive ISO 8601 instant. Required. |
| `end_date`   | Inclusive ISO 8601 instant. Required. |

A missing or unparseable date returns `400` with
`{"error":"start_date and end_date required (ISO 8601)"}`.

`/commerce-metrics` is stricter. Both instants must land on an exact UTC hour. `end_date`
is exclusive, cannot be in the future, and cannot sit more than 366 days after
`start_date`.

`/ads` accepts a second, provider-local form: `date_from` and `date_through` as
`YYYY-MM-DD`. A request that sends both forms returns `400`.

## Response format

Every response is `application/json` and carries `Access-Control-Allow-Origin: *`. Every
error carries a single `error` string. `/ads`, `/commerce-metrics`, and the cost-import
routes add a machine-readable `code` or `reason`. See
[Errors and limits](/api-reference/errors-and-limits).

## Other routes on api.datalyr.com

These four routes sit outside `/v1`, and each takes a different credential.

| Method | Path                           | Credential                               | Purpose                                                                                                                    |
| ------ | ------------------------------ | ---------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- |
| `GET`  | `/health`                      | None                                     | Returns `{"status":"ok"}`.                                                                                                 |
| `POST` | `/mcp`                         | OAuth access token, scope `datalyr:read` | Hosted MCP server. See [Connect Claude or Codex](/datalyr-mcp).                                                            |
| `POST` | `/attribution/lookup`          | Write key in `X-Datalyr-API-Key`         | Resolves web attribution for one email address. The mobile SDKs call it for themselves. Not a read route for your backend. |
| `POST` | `/attribution/deferred-lookup` | Write key in `X-Datalyr-API-Key`         | Resolves a web session by client IP after an app install. The mobile SDKs call it.                                         |

Two `POST` routes exist at `/v1/shopify/economics/cost-imports/validate` and
`/v1/shopify/economics/cost-imports/apply`. We run both of them, so an Agent key on its own
returns `403`.

## Verify your access

1. Copy your Agent key from **Settings → API**.
2. Run the request below in a terminal.

```bash theme={null}
curl "https://api.datalyr.com/v1/workspace" \
  --header "Authorization: Bearer dk_agent_YOUR_KEY"
```

The response names your workspace:

```json theme={null}
{
  "id": "ws_public_id",
  "name": "Acme",
  "domain": "acme.com",
  "timezone": "America/Chicago",
  "connected_platforms": [{ "provider": "stripe", "status": "active" }]
}
```

## When it doesn't work

| Symptom                                      | Cause                                                  | Fix                                                                          |
| -------------------------------------------- | ------------------------------------------------------ | ---------------------------------------------------------------------------- |
| `401` and `Invalid or missing Agent API key` | You sent a write key, or no key at all.                | Send the `dk_agent_` key from **Settings → API**.                            |
| `404` and `Not found`                        | The path is not one of the endpoints above.            | Compare your path with the tables. Every path starts with `/v1`.             |
| `405` and `Method not allowed`               | The path exists, but not for the method you sent.      | Use the method the tables above name for that path.                          |
| `403` and `insufficient_scope`               | Your key does not carry the scope that endpoint needs. | Read `required_scope` on the response. Ask an owner for a key that holds it. |
| `200` with empty arrays and zero totals      | The date range holds no data.                          | Widen the range. Confirm that events arrive in **Events**.                   |

## Next

* [Authentication](/api-reference/authentication): create a key and send it correctly.
* [Errors and limits](/api-reference/errors-and-limits): every status code and the rate limit.
* [Agent access](/api-reference/agents): the write scopes, the dry run, and the undo path.
* [Connect Claude or Codex](/datalyr-mcp): read the same data without handling a key.
