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

# Ingest a URL

> Idempotent submission, progress, cancellation, and what success and failure look like.

## Submit

```bash theme={null}
curl -X POST https://api.refmatter.com/v1/ingestions \
  -H "Authorization: Bearer $REFMATTER_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{"url":"https://www.youtube.com/watch?v=aqz-KE-bpKQ","mediaProfile":"full"}'
```

`Idempotency-Key` is required: 8 to 128 unreserved characters, unique per intent. Replays return the original ingestion; a different body under the same key is `409 idempotency_conflict`.

## Layers

A reference is assembled in layers, each with its own switch and its own timing:

| Layer                           | Request field                          | Default    | Ready after                |
| ------------------------------- | -------------------------------------- | ---------- | -------------------------- |
| Metadata and provenance         | always                                 | on         | seconds                    |
| Transcript from source captions | `transcript`                           | `true`     | seconds, with the metadata |
| Media bytes and poster          | `mediaProfile: none / analysis / full` | `analysis` | minutes, in the background |

The reference becomes readable as soon as the metadata layer is durable: `referenceId` appears on the ingestion while media is still transferring, and the reference reports `mediaAvailability: "processing"` until the bytes are verified. With `mediaProfile: "none"` the media stages are skipped and the reference reports `mediaAvailability: "unavailable"`; a later ingestion of the same URL with `analysis` or `full` adds the bytes to the same reference instead of creating a new one.

## Follow progress

```json theme={null}
{
  "id": "01a0a040-…",
  "state": "running",
  "progress": {
    "stage": "transfer_media",
    "completedStages": [
      "validate_url",
      "resolve_identity",
      "acquire_metadata",
      "discover_media"
    ],
    "attemptNumber": 1,
    "nextAttemptAt": null
  },
  "attempts": [
    { "number": 1, "state": "running", "stages": ["…"], "failureCode": null }
  ],
  "referenceId": "01a0a04b-…",
  "outcomeCode": null
}
```

Stages run in order: `validate_url`, `resolve_identity`, `acquire_metadata`, `persist_canonical`, `create_reference` (the reference is readable from here), then `discover_media`, `transfer_media`, `verify_media`, `finalize` for the media layer. Transient failures are retried with backoff; `attempts` shows each try.

## Outcomes

| `state`     | `outcomeCode`                                                  | Next step                                                                                                                                     |
| ----------- | -------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
| `succeeded` | `null`                                                         | Read `referenceId` (already available once the reference is readable)                                                                         |
| `failed`    | e.g. `unavailable_private`, `limit_exceeded`, `route_disabled` | Do not retry unless `retryable` is true. If the reference was already readable it stays `ready` and only `mediaAvailability` becomes `failed` |
| `cancelled` | `null`                                                         | Nothing; completed effects are never undone                                                                                                   |

## Cancel

`POST /v1/ingestions/{id}/cancel` requests cancellation. The worker yields at the next stage boundary; the ingestion settles as `cancelled`.
