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

# Sources

> Source-first ingestion, provenance records, and source lifecycle operations.

Source reads return authorized records, including their URI/path fields. A
`read:source:raw` distinction is not a universal field-redaction guarantee.
Classify sensitive source locations in the record security envelope. See
[security](/recall/concepts/security).

## Versioned source ingestion

For application synchronization, use the named-space source lifecycle. It binds
an external identity to immutable text revisions, opaque generations and durable
operation receipts. Source writes require `read:source` and `write:source`; reads
require `read:source`. Select the space in the path or `X-Space-Id` as applicable.
See [service setup](/recall/operations/service-setup) for credentials, grants and
processing approvals.

| Method | Path | Purpose |
| - | - | - |
| PUT | `/v1/spaces/{space}/sources/{external_id}` | Accept a new immutable revision |
| GET | `/v1/spaces/{space}/sources/{external_id}/head` | Read desired/published revision and current generation |
| GET | `/v1/spaces/{space}/sources` | Reconcile authorized heads with an opaque cursor |
| GET | `/v1/operations/{operation_id}` | Poll the original operation; select `X-Space-Id` |
| POST | `/v1/spaces/{space}/sources/{external_id}:retract` | Withdraw source support using the current generation |
| POST | `/v1/spaces/{space}/sources/{external_id}:erase` | Physically erase source-derived data under the recovery barrier |

```bash theme={null}
curl -fsS -X PUT http://localhost:3000/v1/spaces/research/sources/meeting-42 \
  -H "Authorization: Bearer $TOKEN" \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: meeting-42-r1' \
  -d '{
    "revision":"r1",
    "text":"Alice owns the Project Cedar test plan.",
    "media_type":"text/plain",
    "source_url":null,
    "observed_at":"2026-10-13T12:00:00Z",
    "labels":{},
    "expected_generation":null
  }'
```

Use actual observation times, authorized security/subject metadata and opaque
revision identifiers. Creation expects a null generation; replacement uses the
exact current generation. Text is nonempty UTF-8, at most 2,000,000 decoded bytes;
`media_type` is `text/plain` or `text/markdown`. Percent-encode each path component
once. Metadata keys prefixed by `_` are reserved. Changing immutable text,
labels or security metadata requires a new revision.

HTTP 202 is durable acceptance, not completion. Poll `operation_id` from that
response with `X-Space-Id: research`, then read the current head. Operation states
are `pending`, `running`, `complete`, `failed_retryable` and `failed_final`.
Completed historical operations do not guarantee that their revision remains
current after replacement or withdrawal. A reused revision with different
immutable payload conflicts. Identical idempotent replay returns the original
operation; on `conflict_generation`, reconcile the head before replacement.

Retraction and erasure take `{"expected_generation":"generation-from-head"}`.
They require an explicit operation; an ordinary retry must not undo them.
Reactivation uses `reactivate:true` on PUT; recreation after erasure uses
`recreate_after_erasure:true`. Both require the current tombstone generation.
Physical erasure removes source-derived payload and retains an independent
payload-free recovery journal; registered backups obey bounded retention.

MCP provides `get_source_head`, `get_source_operation`, `retract_source` and
`erase_source` using the same selected-space and generation contract. Inspect
`tools/list` for their exact arguments. The inventory endpoint is bounded
reconciliation, not a snapshot or lossless change feed. Retain external-to-internal
identity mappings at admission; erased identifiers cannot be recovered from it.

## Retained source-record endpoints

These older ingestion and record APIs remain available with their own contracts.
Use the versioned lifecycle above when synchronizing application revisions.

### Endpoints

| Method | Path | Description |
| - | - | - |
| POST | `/v1/sources/ingest` | Stage a source and enqueue its workflow |
| POST | `/v1/sources/search` | Search source records |
| POST | `/v1/sources` | Create a raw source record |
| GET | `/v1/sources/{id}` | Get source |
| PUT | `/v1/sources/{id}` | Replace source |
| DELETE | `/v1/sources/{id}` | Soft delete source |
| POST | `/v1/sources/{id}/restore` | Restore source |

## Ingest a source

Use the normalized ingest endpoint to generate base fields, stage supplied
content, and enqueue source-first processing:

```bash theme={null}
curl -f -X POST http://localhost:3000/v1/sources/ingest \
  -H "Authorization: Bearer $TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{
    "name": "Project Cedar meeting",
    "source_type": "manual",
    "title": "Project Cedar meeting",
    "text": "Alice and Bob discussed the launch plan.",
    "collection": "meeting-notes",
    "ingest_mode": "graph"
  }'
```

`name` is required. Optional inputs include `uri`, `source_type`, `title`, `text`,
`media`, `collection`, `metadata`, `rendition_key`, `extraction_skill`, `extraction_purpose`, `extraction_domain`,
`ingest_mode`, and `sync`. Supported source types are `email`, `calendar`, `web`,
`drive`, `chat`, `device_frame`, `device_video`, `transcript`, and `manual`.

A request with staged document content defaults to `graph` mode; a source without
it defaults to `metadata`. Other ingest modes are `skip` and `retrieval`. A URI
is provenance metadata, not a promise that this endpoint will fetch and render
arbitrary web content. Supply content for retrieval/graph ingestion.

<Warning>
  `sync` must be omitted or `false`. Synchronous ingest is rejected. A `201`
  response confirms staging and enqueueing, not completed extraction.
</Warning>

The response contains `source_id`, `lifecycle_status`, `current_ingest_mode`,
`work_item_id`, and optional `doc_id`. Follow work and runtime status as described
in [System](/recall/api/system). See [Documents](/recall/api/documents) for media inputs.

## Raw source record format

`POST /v1/sources` creates a record without scheduling the source-first workflow.
It accepts the complete `Source` type, including flattened base fields:

```json source.json theme={null}
{
  "id": "source:550e8400-e29b-41d4-a716-446655440000",
  "space_id": "default",
  "created_at": "2026-01-15T10:30:00Z",
  "updated_at": "2026-01-15T10:30:00Z",
  "created_by": "example-client",
  "provenance": {},
  "security": {"policy_id":"default","level":0},
  "type": "web",
  "hash": "replace-with-the-source-content-hash",
  "external_id": "example-report",
  "uri": "https://example.com/report"
}
```

Here the field is `type`, not the ingest endpoint's `source_type`. Use fresh IDs,
actual timestamps and content hashes, the correct space, and your policy's labels.
Additional fields include `path`, `parent`, `range`, `timestamp`, `author`,
`channel`, `metadata`, and lifecycle information.

## Search and lifecycle

```bash theme={null}
curl -f -X POST http://localhost:3000/v1/sources/search \
  -H "Authorization: Bearer $TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"query":"Project Cedar","limit":10}'
```

Search accepts `query`, `include_deleted` (default `false`), `limit` (default `50`),
and `cursor`. It returns full source records under `results`, plus `cursor`,
`has_more`, and `total`, filtered by the caller's authorization.

`GET` by ID returns the full source and accepts `include_deleted=true`. To update,
read the record, modify its intended fields, and send the complete JSON to
`PUT /v1/sources/{id}`. Keep the ID consistent with the path. This is replacement,
not a partial patch. `DELETE` soft deletes and returns `204`; `POST` to the
`/restore` route restores and returns the record.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.