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

# Ingest

> Push documents directly into Repo without going through a connector.

Use this when you have a custom source not covered by Repo's built-in connectors (e.g. an internal wiki, a proprietary CRM, a folder of PDFs in S3) and you want to feed its documents into Repo's memory.

## Required action

`ingest`

## Body

<ParamField body="documents" type="array" required>
  Array of source documents to ingest. Min 1 entry.
</ParamField>

### Document fields

<ParamField body="connectorId" type="uuid" required>
  The connector this document belongs to. Create a connector first via the console, or contact support for a "virtual" connector for custom sources.
</ParamField>

<ParamField body="provider" type="string" required>
  One of `slack`, `google_drive`, `notion`, `gmail`. For custom sources, pick the one whose access model matches yours.
</ParamField>

<ParamField body="sourceKind" type="string" required>
  One of `message`, `document`, `file`.
</ParamField>

<ParamField body="externalId" type="string" required>
  Stable ID from your source system. Repo dedupes on `(organizationId, provider, externalId)` — re-posting with the same `externalId` is treated as an update.
</ParamField>

<ParamField body="title" type="string" required>
  Human-facing title. Used in citations.
</ParamField>

<ParamField body="body" type="string" required>
  The full text content to embed.
</ParamField>

<ParamField body="url" type="string">
  Deep-link back to the source (will be returned in retrieval hits).
</ParamField>

<ParamField body="authorName" type="string">
  Original author's name.
</ParamField>

<ParamField body="sourceCreatedAt" type="ISO 8601">When the source was created at the provider.</ParamField>

<ParamField body="sourceUpdatedAt" type="ISO 8601">When the source was last updated at the provider.</ParamField>

<ParamField body="metadata" type="object" default="{}">
  Arbitrary provider-specific metadata. If you want to preserve ACLs or other access policy data, include an `accessPolicy` key — Repo's retrieval will surface it back to the caller.
</ParamField>

## Response

```json theme={null}
{
  "ingested": 12,
  "skipped": 0
}
```

`skipped` counts documents that matched an existing `externalId` with identical content (no-op).

## Example

```bash theme={null}
curl -X POST https://api.userepo.com/v1/ingest \
  -H "Authorization: Bearer repo_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "documents": [
      {
        "connectorId": "550e8400-e29b-41d4-a716-446655440000",
        "provider": "notion",
        "sourceKind": "document",
        "externalId": "internal-wiki-page-42",
        "title": "Refund policy",
        "body": "Customers can request a refund within 30 days...",
        "url": "https://wiki.acme.internal/refunds",
        "metadata": {
          "accessPolicy": {
            "provider": "notion",
            "mode": "provider_scope",
            "description": "Wiki page accessible to all employees"
          }
        }
      }
    ]
  }'
```

## When to use ingest vs. a connector

| Use `/v1/ingest` when                                      | Use a built-in connector when                |
| ---------------------------------------------------------- | -------------------------------------------- |
| You have a custom source not covered by connectors         | The source is Slack, Drive, Notion, or Gmail |
| You want to control sync cadence yourself                  | You want hands-off, every-15-minute polling  |
| You're piping in data from an ETL pipeline you already run | You don't want to maintain sync logic        |
