# API reference

Read analytics, push the tracking plan (analytics.yaml), and send server-side events.

- Base URL: `https://twelvebit.app/api/v1`
- OpenAPI 3.1: [https://twelvebit.app/api/v1/openapi.json](https://twelvebit.app/api/v1/openapi.json) (version 1.0.0)

## Authentication

Send a key in the `Authorization` header:

~~~sh
curl -sS https://twelvebit.app/api/v1/overview -H "Authorization: Bearer $TWELVEBIT_API_KEY"
~~~

| Credential | Prefix | Scope |
|---|---|---|
| Site key | `tbk_` | One site. The `site` parameter is ignored. Create it in the site settings, or let `twelvebit init` create it. |
| Account token | `tbt_` | Every site you can open. Pass `?site=<id, public id or domain>` (or `site_id` in the payments body). Create it on [your account page](/account). |
| Session | cookie or Bearer session token | The dashboard session, and the token that `twelvebit init` gets through the device login. Works like an account token. |

Keys are secrets. Use them on servers and in terminals only, never in browser code. Viewers can read; writing (events, identify, payments, tracking plan, annotations, bots) needs a site key or the owner or member role.

## Responses

Success: `{ "data": …, "meta": { … } }`. `meta` names the site and, for analytics, the time range (`range.label`, `range.from`, `range.to`, `range.tz`) and the attribution model (last touch: the visit before the payment).

Error: `{ "error": { "code": "site_required", "message": "…" } }` with a matching HTTP status (400, 401, 403, 404, 422, 429, 500). Messages say what to do next.

## Rate limit

120 requests per minute per key. Above that the API answers `429` with a `Retry-After` header in seconds. Wait that long, then try again.

## Common query parameters

| Parameter | Values | Notes |
|---|---|---|
| `period` | `today`, `yesterday`, `24h`, `7d`, `30d`, `90d`, `12m`, `wtd`, `mtd`, `ytd`, `all`, `custom` | Default `30d`. Days are in the site's time zone. |
| `from`, `to` | `YYYY-MM-DD` | Both together make a custom range. `to` is included. |
| `f_<dim>` | any value | Filter, for example `f_country=DE`, `f_channel=Organic Search`, `f_page=/pricing`, `f_goal=signup`. Dimensions: channel, referrer, campaign, keyword, source, hostname, entry, exit, country, region, city, browser, os, device, page, goal. Filters combine with AND. |
| `site` | id, public id or domain | Needed for account tokens and sessions when you have more than one site. |

Revenue in breakdowns is attributed with the model **last touch: the visit before the payment**.

## Endpoints

| Method | Path | Summary |
|---|---|---|
| `GET` | [`/api/v1/sites`](#get-apiv1sites) | List sites |
| `GET` | [`/api/v1/sites/{siteId}`](#get-apiv1sitessiteid) | Get a site |
| `GET` | [`/api/v1/overview`](#get-apiv1overview) | KPIs for a range |
| `GET` | [`/api/v1/timeseries`](#get-apiv1timeseries) | Visitors and revenue over time |
| `GET` | [`/api/v1/breakdown`](#get-apiv1breakdown) | Top values of one dimension |
| `GET` | [`/api/v1/realtime`](#get-apiv1realtime) | Who is on the site now |
| `GET` | [`/api/v1/goals`](#get-apiv1goals) | Goals and their completions |
| `GET` | [`/api/v1/funnels`](#get-apiv1funnels) | Compute a funnel |
| `GET` | [`/api/v1/people`](#get-apiv1people) | List visitors |
| `GET` | [`/api/v1/people/{visitorId}`](#get-apiv1peoplevisitorid) | One visitor's journey |
| `GET` | [`/api/v1/tracking-plan`](#get-apiv1tracking-plan) | Get the tracking plan |
| `PUT` | [`/api/v1/tracking-plan`](#put-apiv1tracking-plan) | Replace the tracking plan |
| `GET` | [`/api/v1/install-check`](#get-apiv1install-check) | Install check and drift |
| `GET` | [`/api/v1/annotations`](#get-apiv1annotations) | List annotations |
| `POST` | [`/api/v1/annotations`](#post-apiv1annotations) | Add an annotation |
| `POST` | [`/api/v1/events`](#post-apiv1events) | Send a goal from your server |
| `POST` | [`/api/v1/identify`](#post-apiv1identify) | Identify a visitor from your server |
| `POST` | [`/api/v1/payments`](#post-apiv1payments) | Record a payment |
| `GET` | [`/api/v1/bots`](#get-apiv1bots) | Crawler traffic |
| `POST` | [`/api/v1/bots`](#post-apiv1bots) | Record a crawler hit |
| `POST` | [`/api/v1/cli/bootstrap`](#post-apiv1clibootstrap) | Find or create a site and mint a key (CLI) |
| `GET` | [`/api/v1/openapi.json`](#get-apiv1openapijson) | This document |

### GET /api/v1/sites

**List sites.**

Every site the key can read. A site key returns only its own site.

~~~sh
curl -sS "https://twelvebit.app/api/v1/sites" -H "Authorization: Bearer $TWELVEBIT_API_KEY"
~~~

Responses: `200` OK · `401` Missing or invalid key · `429` Rate limited (see Retry-After).

### GET /api/v1/sites/{siteId}

**Get a site.**

One site by id, public id or domain, with its script tag.

| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
| `siteId` | path | string | yes | Site id, public id (tb_…) or domain. |

~~~sh
curl -sS "https://twelvebit.app/api/v1/sites/tb_YOUR_SITE_ID" -H "Authorization: Bearer $TWELVEBIT_API_KEY"
~~~

Responses: `200` OK · `400` Invalid input · `401` Missing or invalid key · `404` Not found · `429` Rate limited (see Retry-After).

### GET /api/v1/overview

**KPIs for a range.**

Visitors, visits, page views, bounce rate, time on site, revenue, customers and conversion, for the range and the previous range of the same length, plus the number of people online now.

| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
| `site` | query | string | no | Site id, public id (tb_…) or domain. Needed for account tokens with more than one site; site keys ignore it. |
| `period` | query | today \| yesterday \| 24h \| 7d \| 30d \| 90d \| 6m \| 12m \| wtd \| mtd \| ytd \| all \| custom | no |  |
| `from` | query | string | no | First day (YYYY-MM-DD, site time zone). Use with `to`. |
| `to` | query | string | no | Last day, inclusive. |
| `f_<dim>` | query | string | no | Filter by one value. Dimensions: channel, referrer, campaign, keyword, source, hostname, entry, exit, country, region, city, browser, os, device, page, goal. |

~~~sh
curl -sS "https://twelvebit.app/api/v1/overview?period=7d" -H "Authorization: Bearer $TWELVEBIT_API_KEY"
~~~

Responses: `200` OK · `400` Invalid input · `401` Missing or invalid key · `404` Not found · `429` Rate limited (see Retry-After).

### GET /api/v1/timeseries

**Visitors and revenue over time.**

One point per hour, day, week or month in the site's time zone.

| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
| `site` | query | string | no | Site id, public id (tb_…) or domain. Needed for account tokens with more than one site; site keys ignore it. |
| `period` | query | today \| yesterday \| 24h \| 7d \| 30d \| 90d \| 6m \| 12m \| wtd \| mtd \| ytd \| all \| custom | no |  |
| `from` | query | string | no | First day (YYYY-MM-DD, site time zone). Use with `to`. |
| `to` | query | string | no | Last day, inclusive. |
| `g` | query | hour \| day \| week \| month | no | Bucket size. Default depends on the range length. |
| `f_<dim>` | query | string | no | Filter by one value. Dimensions: channel, referrer, campaign, keyword, source, hostname, entry, exit, country, region, city, browser, os, device, page, goal. |

~~~sh
curl -sS "https://twelvebit.app/api/v1/timeseries?period=7d" -H "Authorization: Bearer $TWELVEBIT_API_KEY"
~~~

Responses: `200` OK · `400` Invalid input · `401` Missing or invalid key · `404` Not found · `429` Rate limited (see Retry-After).

### GET /api/v1/breakdown

**Top values of one dimension.**

Visitors and attributed revenue (last touch: the visit before the payment) per value, sorted by visitors. dim=goal adds completions; dim=referrer adds the channel; dim=region and dim=city add the country.

| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
| `site` | query | string | no | Site id, public id (tb_…) or domain. Needed for account tokens with more than one site; site keys ignore it. |
| `period` | query | today \| yesterday \| 24h \| 7d \| 30d \| 90d \| 6m \| 12m \| wtd \| mtd \| ytd \| all \| custom | no |  |
| `from` | query | string | no | First day (YYYY-MM-DD, site time zone). Use with `to`. |
| `to` | query | string | no | Last day, inclusive. |
| `dim` | query | channel \| referrer \| campaign \| keyword \| source \| hostname \| entry \| exit \| country \| region \| city \| browser \| os \| device \| page \| goal \| outbound | no |  |
| `limit` | query | integer | no |  |
| `f_<dim>` | query | string | no | Filter by one value. Dimensions: channel, referrer, campaign, keyword, source, hostname, entry, exit, country, region, city, browser, os, device, page, goal. |

~~~sh
curl -sS "https://twelvebit.app/api/v1/breakdown?period=7d" -H "Authorization: Bearer $TWELVEBIT_API_KEY"
~~~

Responses: `200` OK · `400` Invalid input · `401` Missing or invalid key · `404` Not found · `429` Rate limited (see Retry-After).

### GET /api/v1/realtime

**Who is on the site now.**

People active in the last 5 minutes (with page, source, place and goals), visitors per minute for the last 30 minutes (oldest first), and the last 30 minutes of events and payments.

| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
| `site` | query | string | no | Site id, public id (tb_…) or domain. Needed for account tokens with more than one site; site keys ignore it. |

~~~sh
curl -sS "https://twelvebit.app/api/v1/realtime" -H "Authorization: Bearer $TWELVEBIT_API_KEY"
~~~

Responses: `200` OK · `400` Invalid input · `401` Missing or invalid key · `404` Not found · `429` Rate limited (see Retry-After).

### GET /api/v1/goals

**Goals and their completions.**

Visitors, completions and revenue of those visitors per goal. `declared` says whether the goal is in the tracking plan; `undeclared` and `not_seen` show drift.

| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
| `site` | query | string | no | Site id, public id (tb_…) or domain. Needed for account tokens with more than one site; site keys ignore it. |
| `period` | query | today \| yesterday \| 24h \| 7d \| 30d \| 90d \| 6m \| 12m \| wtd \| mtd \| ytd \| all \| custom | no |  |
| `from` | query | string | no | First day (YYYY-MM-DD, site time zone). Use with `to`. |
| `to` | query | string | no | Last day, inclusive. |
| `f_<dim>` | query | string | no | Filter by one value. Dimensions: channel, referrer, campaign, keyword, source, hostname, entry, exit, country, region, city, browser, os, device, page, goal. |

~~~sh
curl -sS "https://twelvebit.app/api/v1/goals?period=7d" -H "Authorization: Bearer $TWELVEBIT_API_KEY"
~~~

Responses: `200` OK · `400` Invalid input · `401` Missing or invalid key · `404` Not found · `429` Rate limited (see Retry-After).

### GET /api/v1/funnels

**Compute a funnel.**

With `steps`, computes that funnel: each step counts visitors who reached it after the step before. Without `steps`, computes every funnel saved in the dashboard and every funnel in the tracking plan (`name` picks one).

| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
| `site` | query | string | no | Site id, public id (tb_…) or domain. Needed for account tokens with more than one site; site keys ignore it. |
| `period` | query | today \| yesterday \| 24h \| 7d \| 30d \| 90d \| 6m \| 12m \| wtd \| mtd \| ytd \| all \| custom | no |  |
| `from` | query | string | no | First day (YYYY-MM-DD, site time zone). Use with `to`. |
| `to` | query | string | no | Last day, inclusive. |
| `steps` | query | string | no | 2 to 8 comma-separated steps: `page:/path` (use * as a wildcard) or `goal:name` (a bare name is a goal). |
| `name` | query | string | no | Name of a saved or planned funnel. |
| `f_<dim>` | query | string | no | Filter by one value. Dimensions: channel, referrer, campaign, keyword, source, hostname, entry, exit, country, region, city, browser, os, device, page, goal. |

~~~sh
curl -sS "https://twelvebit.app/api/v1/funnels?period=7d" -H "Authorization: Bearer $TWELVEBIT_API_KEY"
~~~

Responses: `200` OK · `400` Invalid input · `401` Missing or invalid key · `404` Not found · `429` Rate limited (see Retry-After).

### GET /api/v1/people

**List visitors.**

Visitors seen in the range, newest first or by revenue. Anonymous visitors get a stable friendly name like amber-fox-4821.

| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
| `site` | query | string | no | Site id, public id (tb_…) or domain. Needed for account tokens with more than one site; site keys ignore it. |
| `period` | query | today \| yesterday \| 24h \| 7d \| 30d \| 90d \| 6m \| 12m \| wtd \| mtd \| ytd \| all \| custom | no |  |
| `from` | query | string | no | First day (YYYY-MM-DD, site time zone). Use with `to`. |
| `to` | query | string | no | Last day, inclusive. |
| `filter` | query | all \| customers \| identified | no |  |
| `q` | query | string | no | Search email or name (contains), or an exact visitor id or user id. |
| `sort` | query | recent \| revenue | no |  |
| `limit` | query | integer | no |  |
| `offset` | query | integer | no |  |

~~~sh
curl -sS "https://twelvebit.app/api/v1/people?period=7d" -H "Authorization: Bearer $TWELVEBIT_API_KEY"
~~~

Responses: `200` OK · `400` Invalid input · `401` Missing or invalid key · `404` Not found · `429` Rate limited (see Retry-After).

### GET /api/v1/people/{visitorId}

**One visitor's journey.**

Profile, attribution (first touch and the credited visit: last touch: the visit before the payment), every visit with its events, and payments.

| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
| `visitorId` | path | string | yes | Visitor id (_tb_vid) or the user_id from identify. |
| `site` | query | string | no | Site id, public id (tb_…) or domain. Needed for account tokens with more than one site; site keys ignore it. |

~~~sh
curl -sS "https://twelvebit.app/api/v1/people/VISITOR_ID" -H "Authorization: Bearer $TWELVEBIT_API_KEY"
~~~

Responses: `200` OK · `400` Invalid input · `401` Missing or invalid key · `404` Not found · `429` Rate limited (see Retry-After).

### GET /api/v1/tracking-plan

**Get the tracking plan.**

The stored plan as JSON and YAML. Send `Accept: text/yaml` (or ?format=yaml) to get only the YAML text. The YAML is rebuilt from the stored plan, so comments are not kept.

| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
| `site` | query | string | no | Site id, public id (tb_…) or domain. Needed for account tokens with more than one site; site keys ignore it. |
| `format` | query | json \| yaml | no |  |

~~~sh
curl -sS "https://twelvebit.app/api/v1/tracking-plan" -H "Authorization: Bearer $TWELVEBIT_API_KEY"
~~~

Responses: `200` OK · `400` Invalid input · `401` Missing or invalid key · `404` Not found · `429` Rate limited (see Retry-After).

### PUT /api/v1/tracking-plan

**Replace the tracking plan.**

Send analytics.yaml as the body (`Content-Type: text/yaml`), or the same plan as JSON. The schema is strict: unknown keys are errors. The response lists added, removed and changed events. `?dry_run=1` validates without saving.

| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
| `site` | query | string | no | Site id, public id (tb_…) or domain. Needed for account tokens with more than one site; site keys ignore it. |
| `dry_run` | query | boolean | no |  |

Request body:

~~~yaml
version: 1
project: example.com
events:
  signup:
    description: A visitor creates an account
    props:
      plan: { type: string, enum: [free, pro] }
~~~

~~~sh
curl -sS -X PUT "https://twelvebit.app/api/v1/tracking-plan" -H "Authorization: Bearer $TWELVEBIT_API_KEY" \
  -H "Content-Type: text/yaml" --data-binary @analytics.yaml
~~~

Responses: `200` OK · `400` Invalid input · `401` Missing or invalid key · `403` Not allowed for this key · `404` Not found · `422` Invalid tracking plan · `429` Rate limited (see Retry-After).

### GET /api/v1/install-check

**Install check and drift.**

What the site has sent: first and last event, the last 10 events, counts by type in the last 24 hours, host names seen, and for each event in the tracking plan whether it has been seen in the last 30 days. `undeclared` lists goals that were sent but are not in the plan. `next_steps` says what to fix.

| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
| `site` | query | string | no | Site id, public id (tb_…) or domain. Needed for account tokens with more than one site; site keys ignore it. |

~~~sh
curl -sS "https://twelvebit.app/api/v1/install-check" -H "Authorization: Bearer $TWELVEBIT_API_KEY"
~~~

Responses: `200` OK · `400` Invalid input · `401` Missing or invalid key · `404` Not found · `429` Rate limited (see Retry-After).

### GET /api/v1/annotations

**List annotations.**

Notes shown on the time charts in the range.

| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
| `site` | query | string | no | Site id, public id (tb_…) or domain. Needed for account tokens with more than one site; site keys ignore it. |
| `period` | query | today \| yesterday \| 24h \| 7d \| 30d \| 90d \| 6m \| 12m \| wtd \| mtd \| ytd \| all \| custom | no |  |
| `from` | query | string | no | First day (YYYY-MM-DD, site time zone). Use with `to`. |
| `to` | query | string | no | Last day, inclusive. |

~~~sh
curl -sS "https://twelvebit.app/api/v1/annotations?period=7d" -H "Authorization: Bearer $TWELVEBIT_API_KEY"
~~~

Responses: `200` OK · `400` Invalid input · `401` Missing or invalid key · `404` Not found · `429` Rate limited (see Retry-After).

### POST /api/v1/annotations

**Add an annotation.**

Mark a launch, a deploy or a price change on the charts. `ts` defaults to now.

| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
| `site` | query | string | no | Site id, public id (tb_…) or domain. Needed for account tokens with more than one site; site keys ignore it. |

Request body:

~~~json
{
  "label": "Launched on Product Hunt",
  "ts": "2026-10-01T09:00:00Z",
  "url": "https://www.producthunt.com/posts/example"
}
~~~

~~~sh
curl -sS -X POST "https://twelvebit.app/api/v1/annotations" -H "Authorization: Bearer $TWELVEBIT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"label":"Launched on Product Hunt","ts":"2026-10-01T09:00:00Z","url":"https://www.producthunt.com/posts/example"}'
~~~

Responses: `201` Created · `400` Invalid input · `401` Missing or invalid key · `403` Not allowed for this key · `404` Not found · `429` Rate limited (see Retry-After).

### POST /api/v1/events

**Send a goal from your server.**

Records a goal for a visitor that the browser script has already seen. Pass `visitor_id` (the `_tb_vid` cookie) or `user_id` (sent earlier with identify). The goal joins the visitor's latest visit. Names are lowercased; use snake_case.

| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
| `site` | query | string | no | Site id, public id (tb_…) or domain. Needed for account tokens with more than one site; site keys ignore it. |

Request body:

~~~json
{
  "name": "subscription_started",
  "user_id": "user_123",
  "props": {
    "plan": "pro"
  }
}
~~~

~~~sh
curl -sS -X POST "https://twelvebit.app/api/v1/events" -H "Authorization: Bearer $TWELVEBIT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name":"subscription_started","user_id":"user_123","props":{"plan":"pro"}}'
~~~

Responses: `202` Recorded · `400` Invalid input · `401` Missing or invalid key · `403` Not allowed for this key · `404` Not found · `429` Rate limited (see Retry-After).

### POST /api/v1/identify

**Identify a visitor from your server.**

Attaches your user id, email, name, avatar and traits to a visitor. Later server-side events can then use `user_id`.

| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
| `site` | query | string | no | Site id, public id (tb_…) or domain. Needed for account tokens with more than one site; site keys ignore it. |

Request body:

~~~json
{
  "visitor_id": "k3j5h2g7f9d1s8a4q6w0e2r5",
  "user_id": "user_123",
  "email": "ada@example.com",
  "name": "Ada",
  "traits": {
    "plan": "pro"
  }
}
~~~

~~~sh
curl -sS -X POST "https://twelvebit.app/api/v1/identify" -H "Authorization: Bearer $TWELVEBIT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"visitor_id":"k3j5h2g7f9d1s8a4q6w0e2r5","user_id":"user_123","email":"ada@example.com","name":"Ada","traits":{"plan":"pro"}}'
~~~

Responses: `200` OK · `400` Invalid input · `401` Missing or invalid key · `403` Not allowed for this key · `404` Not found · `429` Rate limited (see Retry-After).

### POST /api/v1/payments

**Record a payment.**

For payment providers without a built-in connection. Safe to repeat: `transaction_id` makes it idempotent. The payment is credited to a visit (last touch: the visit before the payment) through `visitor_id`/`session_id`, or through the email or customer id of an identified visitor. An amount of 0 is a free trial and is not counted. Account tokens pass `site_id` in the body.

Request body:

~~~json
{
  "amount": 49,
  "currency": "USD",
  "transaction_id": "inv_1042",
  "visitor_id": "k3j5h2g7f9d1s8a4q6w0e2r5",
  "email": "ada@example.com",
  "plan": "Pro"
}
~~~

~~~sh
curl -sS -X POST "https://twelvebit.app/api/v1/payments" -H "Authorization: Bearer $TWELVEBIT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"amount":49,"currency":"USD","transaction_id":"inv_1042","visitor_id":"k3j5h2g7f9d1s8a4q6w0e2r5","email":"ada@example.com","plan":"Pro"}'
~~~

Responses: `200` Already recorded (duplicate transaction_id), or a free trial · `201` Recorded · `400` Invalid input · `401` Missing or invalid key · `403` Not allowed for this key · `404` Not found · `429` Rate limited (see Retry-After).

### GET /api/v1/bots

**Crawler traffic.**

Hits from AI and search crawlers in the range, by kind (ai_answer, indexing, training) and by bot.

| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
| `site` | query | string | no | Site id, public id (tb_…) or domain. Needed for account tokens with more than one site; site keys ignore it. |
| `period` | query | today \| yesterday \| 24h \| 7d \| 30d \| 90d \| 6m \| 12m \| wtd \| mtd \| ytd \| all \| custom | no |  |
| `from` | query | string | no | First day (YYYY-MM-DD, site time zone). Use with `to`. |
| `to` | query | string | no | Last day, inclusive. |

~~~sh
curl -sS "https://twelvebit.app/api/v1/bots?period=7d" -H "Authorization: Bearer $TWELVEBIT_API_KEY"
~~~

Responses: `200` OK · `400` Invalid input · `401` Missing or invalid key · `404` Not found · `429` Rate limited (see Retry-After).

### POST /api/v1/bots

**Record a crawler hit.**

Crawlers do not run JavaScript, so your server reports them. Send the user agent and path of a request; known crawlers are stored as ai_answer (an assistant fetches the page for a person now), indexing (search engines) or training (model training). Unknown user agents are ignored. Fire and forget: do not make the page wait.

| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
| `site` | query | string | no | Site id, public id (tb_…) or domain. Needed for account tokens with more than one site; site keys ignore it. |

Request body:

~~~json
{
  "user_agent": "Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko); compatible; ChatGPT-User/1.0; +https://openai.com/bot",
  "path": "/pricing"
}
~~~

~~~sh
curl -sS -X POST "https://twelvebit.app/api/v1/bots" -H "Authorization: Bearer $TWELVEBIT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"user_agent":"Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko); compatible; ChatGPT-User/1.0; +https://openai.com/bot","path":"/pricing"}'
~~~

Responses: `202` Accepted · `400` Invalid input · `401` Missing or invalid key · `403` Not allowed for this key · `404` Not found · `429` Rate limited (see Retry-After).

### POST /api/v1/cli/bootstrap

**Find or create a site and mint a key (CLI).**

Used by `twelvebit init` after the device sign-in. Needs a session (the device flow's access token as a Bearer). Finds the user's site for the domain, or creates it, and returns a new site key named after the machine.

Request body:

~~~json
{
  "domain": "example.com",
  "timezone": "Europe/Berlin",
  "hostname": "ada-mbp"
}
~~~

~~~sh
curl -sS -X POST "https://twelvebit.app/api/v1/cli/bootstrap" -H "Authorization: Bearer $TWELVEBIT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"domain":"example.com","timezone":"Europe/Berlin","hostname":"ada-mbp"}'
~~~

Responses: `200` OK · `400` Invalid input · `401` Missing or invalid key · `429` Rate limited (see Retry-After).

### GET /api/v1/openapi.json

**This document.**

~~~sh
curl -sS "https://twelvebit.app/api/v1/openapi.json" -H "Authorization: Bearer $TWELVEBIT_API_KEY"
~~~

Responses: `200` OpenAPI 3.1 document.

---

Source: https://twelvebit.app/docs/api · All docs: https://twelvebit.app/llms.txt
