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

# API reference

> Base URL, auth, conventions, and every endpoint.

The Cue API is a small, predictable REST API over HTTPS. Everything speaks JSON and every call
authenticates with an API key.

There are five workflows, and each has a guide page with worked examples:

| Workflow                  | Does                                    | Endpoints                                    |
| ------------------------- | --------------------------------------- | -------------------------------------------- |
| [Match](/docs/match)           | Rank licensed tracks against a video.   | `POST /v1/match` · `GET /v1/match/jobs/{id}` |
| [Underscore](/docs/underscore) | Rank tracks against a script, no video. | `POST /v1/underscore`                        |
| [Sync](/docs/sync)             | Where a track lands against a video.    | `POST /v1/align` · `GET /v1/align/jobs/{id}` |
| [Loop](/docs/loop)             | Cover a clip longer than the track.     | `POST /v1/loop` · `GET /v1/loop/jobs/{id}`   |
| [Beats](/docs/beats)           | Beat and downbeat grid for any audio.   | `POST /v1/beats` · `GET /v1/beats/{job_id}`  |

Three of the five take a video, so there's one more group underneath them: `POST /v1/videos` ·
`POST /v1/videos/{id}/ingest` · `GET /v1/videos/{id}`. Analysis is billed there, once, per minute
of the clip. The resulting `video_id` is reused by every match, sync and loop against it.

Beats stands apart: it takes audio you point us at, never a `video_id`, and it has its own queue,
worker and meter.

## Base URL

```
https://cue.vibeset.ai
```

All endpoints live under `/v1`. There's no separate sandbox host and no sandbox mode: customer
keys are always `vbsk_live_`, and every call is billed at the rates in
[Metering & pricing](/docs/metering-and-pricing). See [Authentication](/docs/authentication).

## Authentication

Every request carries your API key, either way round. Use whichever your client reaches for:

```bash theme={null}
Authorization: Bearer vbsk_live_...
```

```bash theme={null}
x-api-key: vbsk_live_...
```

The same key authenticates the [MCP server](/docs/mcp-server), where Bearer is what most clients ask
for by default. See [Authentication](/docs/authentication) for rotation and revocation.

## Conventions

<CardGroup cols={2}>
  <Card title="JSON in, JSON out" icon="brackets-curly">
    Send `content-type: application/json`. Every response is JSON, success or error.
  </Card>

  <Card title="Stable IDs" icon="fingerprint">
    A `video_id` is yours to reuse across match, sync, and loop. A `track_id` (from a match)
    resolves in your catalog.
  </Card>

  <Card title="Request IDs" icon="hashtag">
    Every response carries `X-Request-Id`. Include it when you contact support.
  </Card>

  <Card title="Timings, not recordings" icon="waveform">
    Responses carry catalog identifiers and analysis. No audio is attached by default. See
    [Preview audio](/docs/own-license#preview-audio).
  </Card>

  <Card title="Expand" icon="list">
    `?expand=full` on a match or underscore returns the full track object and the lane's
    candidate pool instead of the lean shape.
  </Card>
</CardGroup>

## Idempotency

`Idempotency-Key` is accepted on five endpoints, and it does **two different things** depending on
which one you send it to.

On `POST /v1/beats` it is a true idempotency key: a repeat with the same key returns the original
`job_id` and starts no second job, a key is honoured for 24 hours, and reusing one for a different
request is a `422`.

On `POST /v1/match`, `/v1/underscore`, `/v1/align` and `/v1/loop` it only suppresses a duplicate
row in the billing ledger. The request itself re-executes in full and you get a freshly computed
response, so a retry is safe on your bill but is not free of work. Send a **new** key per distinct
request and reuse a key only when retrying that exact request. A key you pin across different
calls will silently suppress the billing for all of them.

`POST /v1/videos` and `POST /v1/videos/{id}/ingest` do not accept the header at all. Those are the
calls that trigger the per-minute analysis charge, and the meter dedupes them on a server-generated
key of its own.

## Synchronous vs. async

Work that's already warm returns **`200`** immediately. Work that needs a first-time build returns
**`202`**, so a long build never trips a request timeout. There are two `202` shapes and they are
not interchangeable.

A **job** carries a `job_id` and a job URL. That covers a match on a video whose analysis isn't
cached, a cold align, a cold loop, and any beats call:

```json 202 theme={null}
{
  "video_id": "69e5a58e73370adad5a10dc9",
  "status": "processing",
  "job_id": "run_9f2c…",
  "poll": "/v1/match/jobs/run_9f2c…",
  "next": "Poll the job until status is 'complete', then read its lane + matches."
}
```

A **video that is still being ingested** carries no `job_id` at all, and points at the video:

```json 202 theme={null}
{ "video_id": "69e5a58e73370adad5a10dc9", "status": "processing", "poll": "/v1/videos/69e5a58e73370adad5a10dc9" }
```

`POST /v1/videos`, and `POST /v1/match` called with a `video_url`, both return that second shape.
Branch on whether `job_id` is present rather than on the status code, and follow the `poll` path
you were actually given.

Poll until `status` is `complete`. Jobs are **billed once, on completion**, and polling is free. Each
family has its own job endpoint: `GET /v1/match/jobs/{id}`, `/v1/align/jobs/{id}`,
`/v1/loop/jobs/{id}`, `/v1/beats/{job_id}`.

## Errors & rate limits

Every `/v1` error returns a JSON `error` object with a stable `type`, a human-readable `message`
and the `request_id` for that call. A `422` from request validation adds a `details` array. Keys
are rate limited per key, 60 requests per minute by default. See
[Errors & rate limits](/docs/errors-and-limits) for the full table.

## Endpoints

Pick any endpoint from the sidebar for its parameters, schema, and a live **Try it** panel. The
workflow table at the top of this page maps each one to the guide that shows it in use.

The reference covers every endpoint you can call with an API key. Two `/v1` routes are left out
because you can't: the Stripe webhooks, which authenticate by signature rather than by key.

Prefer an agent to writing HTTP? The same surface is available over [MCP](/docs/mcp-server), where each
tool wraps one endpoint and bills identically.
