Documents
Upload, confirm, sync, crawl, and remove the documents a project answers from. Ingestion is a multi-step flow, not a single POST.
All 18 endpoints on this page require a Cognito JWT from a signed-in dashboard session. An integration key returns 401 on every one of them — see what an API key can reach.
/v1/projects/{projectId}/documents/upload-url Presigned upload URL (single file). Response is client-sanitized — no S3 keys or bucket names.
Auth Cognito JWT member
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
projectId | string | Required | Project the request applies to. Must belong to the calling workspace. |
Query parameters
None.Everything it needs is in the request body.
Body parameters
| Name | Type | Required | Description |
|---|---|---|---|
filename | string | Required | Original file name. Its extension decides the parser. |
contentType | string | Optional | MIME type of the file, e.g. application/pdf. |
Request
{
"filename": "refund-policy.pdf",
"contentType": "application/pdf"
} Response
200 Success
{
"documentId": "doc_abc123",
"filename": "refund-policy.pdf",
"uploadUrl": "https://...",
"metadataUploadUrl": "https://...",
"metadataBody": "{\"metadataAttributes\":{\"tenant_id\":\"...\",\"project_id\":\"...\"}}",
"expiresInSeconds": 900,
"instructions": "1) PUT file to uploadUrl. 2) PUT metadataBody to metadataUploadUrl. 3) POST confirm. 4) POST sync."
} Status codes
| Status | Meaning |
|---|---|
| 200 | Success. |
| 400 | The request body failed validation. |
| 401 | Missing or expired JWT. |
| 402 | The workspace is at its plan's storage cap. Delete documents or upgrade. See plan limits. |
| 403 | The signed-in user's role does not allow this. |
| 404 | No such resource in this workspace. |
| 429 | Rate limited. See rate limits. |
curl
curl -X POST 'https://api.oprag.ai/v1/projects/{projectId}/documents/upload-url' \
-H 'Authorization: Bearer <Cognito JWT>' \
-H 'Content-Type: application/json' \
-d '{"filename": "refund-policy.pdf","contentType": "application/pdf"}' /v1/projects/{projectId}/documents/bulk-upload-url Presigned upload URLs (batch)
Auth Cognito JWT member
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
projectId | string | Required | Project the request applies to. Must belong to the calling workspace. |
Query parameters
None.Everything it needs is in the request body.
Body parameters
| Name | Type | Required | Description |
|---|---|---|---|
files | object[] | Optional | One entry per file: filename and contentType. |
Response
Status codes
| Status | Meaning |
|---|---|
| 200 | Success. |
| 400 | The request body failed validation. |
| 401 | Missing or expired JWT. |
| 402 | The workspace is at its plan's storage cap. Delete documents or upgrade. See plan limits. |
| 403 | The signed-in user's role does not allow this. |
| 404 | No such resource in this workspace. |
| 429 | Rate limited. See rate limits. |
curl
curl -X POST 'https://api.oprag.ai/v1/projects/{projectId}/documents/bulk-upload-url' \
-H 'Authorization: Bearer <Cognito JWT>' /v1/projects/{projectId}/documents/{documentId}/confirm Confirm S3 upload
Auth Cognito JWT member
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
projectId | string | Required | Project the request applies to. Must belong to the calling workspace. |
documentId | string | Required | Document within the project. |
Query parameters
None.Acts on the resource named in the path; there is nothing to choose.
Body parameters
None.Confirms the upload identified by the path; everything needed is already on the job.
Response
Status codes
curl
curl -X POST 'https://api.oprag.ai/v1/projects/{projectId}/documents/{documentId}/confirm' \
-H 'Authorization: Bearer <Cognito JWT>' /v1/projects/{projectId}/documents/{documentId}/replace-url Replace document (versioned)
Auth Cognito JWT member
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
projectId | string | Required | Project the request applies to. Must belong to the calling workspace. |
documentId | string | Required | Document within the project. |
Query parameters
None.Everything it needs is in the request body.
Body parameters
| Name | Type | Required | Description |
|---|---|---|---|
filename | string | Optional | New file name. Defaults to the existing document's. |
contentType | string | Optional | MIME type of the file, e.g. application/pdf. |
Response
Status codes
curl
curl -X POST 'https://api.oprag.ai/v1/projects/{projectId}/documents/{documentId}/replace-url' \
-H 'Authorization: Bearer <Cognito JWT>' /v1/projects/{projectId}/documents/{documentId}/versions List document version history
Auth Cognito JWT member
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
projectId | string | Required | Project the request applies to. Must belong to the calling workspace. |
documentId | string | Required | Document within the project. |
Query parameters
None.Neither paginated nor filtered from the query string.
Body parameters
None.GET requests carry no body.
Response
Status codes
curl
curl -X GET 'https://api.oprag.ai/v1/projects/{projectId}/documents/{documentId}/versions' \
-H 'Authorization: Bearer <Cognito JWT>' /v1/projects/{projectId}/documents/{documentId}/preview-url Presigned preview URL (5 min TTL, works without RAG)
Auth Cognito JWT member
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
projectId | string | Required | Project the request applies to. Must belong to the calling workspace. |
documentId | string | Required | Document within the project. |
Query parameters
None.Reads the resource named in the path; there is nothing else to select.
Body parameters
None.GET requests carry no body.
Response
Status codes
curl
curl -X GET 'https://api.oprag.ai/v1/projects/{projectId}/documents/{documentId}/preview-url' \
-H 'Authorization: Bearer <Cognito JWT>' /v1/projects/{projectId}/documents List documents. Each item is DocumentApiResponse — client-safe fields only (no S3 keys or tenant internals).
Auth Cognito JWT member
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
projectId | string | Required | Project the request applies to. Must belong to the calling workspace. |
Query parameters
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
limit | number | Optional | 50 | Documents per page. Values above 100 are clamped to 100; a non-numeric or non-positive value falls back to 50. |
Body parameters
None.GET requests carry no body.
Response
200 Success
{
"documents": [
{
"documentId": "doc_123",
"projectId": "proj_abc123",
"filename": "refund-policy.pdf",
"status": "indexed",
"contentType": "application/pdf",
"sizeBytes": 245760,
"uploadedAt": "2026-06-28T12:00:00.000Z",
"updatedAt": "2026-06-28T12:05:00.000Z",
"sourceType": "upload"
}
]
} Status codes
curl
curl -X GET 'https://api.oprag.ai/v1/projects/{projectId}/documents' \
-H 'Authorization: Bearer <Cognito JWT>' /v1/projects/{projectId}/documents/{documentId} Delete document (S3 + KB)
Auth Cognito JWT member
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
projectId | string | Required | Project the request applies to. Must belong to the calling workspace. |
documentId | string | Required | Document within the project. |
Query parameters
None.Acts on the resource named in the path; there is nothing to choose.
Body parameters
None.DELETE requests carry no body.
Response
Status codes
curl
curl -X DELETE 'https://api.oprag.ai/v1/projects/{projectId}/documents/{documentId}' \
-H 'Authorization: Bearer <Cognito JWT>' /v1/projects/{projectId}/documents/sync Ingest the project's uploaded documents into the knowledge base, returning a runId to track the sync run.
Auth Cognito JWT member
Before you call it
- The response carries
runId. Track the ingestion's progress and outcome throughGET /v1/projects/{projectId}/sync-runs. - Bedrock indexes asynchronously, so the run is still
runningwhen this returns — the documents sit indocumentsPendinguntil the index-status poller finalizes it. - A sync requested while another run is still in flight attaches to that run and returns its
runId; a second run is not started. - A request with nothing ready to ingest is a 400 and records no run at all.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
projectId | string | Required | Project the request applies to. Must belong to the calling workspace. |
Query parameters
None.Acts on the resource named in the path; there is nothing to choose.
Body parameters
None.Starts ingestion for the project's pending documents.
Response
200 Success
{
"documentCount": 2,
"statuses": ["STARTING"],
"companyId": "co_abc123",
"projectId": "proj_abc123",
"runId": "sync_abc123",
"message": "Project-scoped ingestion started. Wait 1–5 minutes before chatting."
} Status codes
| Status | Meaning |
|---|---|
| 202 | Ingestion started. The body carries runId — poll GET …/sync-runs for the outcome. |
| 400 | No uploaded documents ready for ingestion — nothing was pending, and no run is recorded. |
| 401 | Missing or expired JWT. |
| 403 | The signed-in user's role does not allow this. |
| 404 | No such resource in this workspace. |
| 429 | Rate limited. See rate limits. |
| 502 | Failed to start ingestion — the ingestion trigger failed. The cause is in the service logs, not the body. |
| 503 | RAG is not enabled in this environment. |
curl
curl -X POST 'https://api.oprag.ai/v1/projects/{projectId}/documents/sync' \
-H 'Authorization: Bearer <Cognito JWT>' /v1/projects/{projectId}/sync-runs Recent knowledge-base ingestion runs for a project, newest first — powers the dashboard's live sync status and history.
Auth Cognito JWT member
Before you call it
- A sync run is one Bedrock knowledge-base ingestion pass. This is what powers the dashboard's live sync status and history: a
runningrun appears immediately, so polling it tracks an ingestion started byPOST …/documents/sync. triggerismanual(the API or dashboard),connector(a connector source's sync),crawl(a crawl'sautoSync), ordeploy(ingestion run as part of a deploy).connectorSourceIdis present only on connector-triggered runs.statusrunsrunning→completed,partial,failed, ortimeout.partialmeans some documents indexed and others failed;timeoutmeans the poller stopped waiting, and indexing may still finish in the background.documentsSubmittedis set from the start.documentsIndexed,documentsFailed,documentsPending,error,finishedAt, anddurationMsappear only once known — arunningrun carriesdocumentsPendingand no finish time.- Match a row to the sync that started it with the
runIdfromPOST …/documents/sync. - Runs are retained for about 90 days, then expire.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
projectId | string | Required | Project the request applies to. Must belong to the calling workspace. |
Query parameters
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
limit | number | Optional | 20 | Most recent runs to return, newest first. Values above 50 are clamped to 50; a non-numeric or non-positive value falls back to 20. |
Body parameters
None.GET requests carry no body.
Response
200 Success
{
"runs": [
{
"runId": "sync_abc123",
"trigger": "manual",
"status": "completed",
"documentsSubmitted": 12,
"documentsIndexed": 12,
"documentsFailed": 0,
"documentsPending": 0,
"startedAt": "2026-08-27T09:00:00.000Z",
"finishedAt": "2026-08-27T09:02:10.000Z",
"durationMs": 130000
},
{
"runId": "sync_def456",
"trigger": "connector",
"status": "partial",
"connectorSourceId": "src_789",
"documentsSubmitted": 8,
"documentsIndexed": 6,
"documentsFailed": 2,
"documentsPending": 0,
"error": "2 documents failed to index: unsupported encrypted PDF",
"startedAt": "2026-08-26T09:00:00.000Z",
"finishedAt": "2026-08-26T09:01:05.000Z",
"durationMs": 65000
}
]
} Status codes
curl
curl -X GET 'https://api.oprag.ai/v1/projects/{projectId}/sync-runs' \
-H 'Authorization: Bearer <Cognito JWT>' /v1/projects/{projectId}/documents/crawl Crawl a public website (same domain, BFS) and create markdown documents per page, returning a runId to track the run. autoSync defaults to false.
Auth Cognito JWT member
Before you call it
- Both branches carry
runId. Track the run's progress and outcome throughGET /v1/projects/{projectId}/crawl-runs. - Which branch answers depends on the environment: when the event bus is wired the crawl is queued and answered with 202; otherwise it runs inline and answers 200 with the crawled documents.
- Setting
autoSyncregisters a crawl source that re-crawls everysyncIntervalHours; scheduled re-crawls appear in the run history withtrigger: "scheduled".
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
projectId | string | Required | Project the request applies to. Must belong to the calling workspace. |
Query parameters
None.Everything it needs is in the request body.
Body parameters
| Name | Type | Required | Description |
|---|---|---|---|
url | string | Required | Page to start crawling from. Must be HTTP or HTTPS. |
maxPages | number | Optional | Upper bound on pages fetched in this crawl. |
autoSync | boolean | Optional | Re-crawl on a schedule rather than once. |
syncIntervalHours | number | Optional | Hours between automatic re-crawls when autoSync is set. |
Request
{
"url": "https://docs.example.com",
"maxPages": 50,
"autoSync": false,
"syncIntervalHours": 24
} Responses
200 Inline crawl
The crawl ran synchronously. documents are uploaded but not yet indexed — call POST …/documents/sync to ingest them.
{
"projectId": "proj_abc",
"companyId": "co_xyz",
"runId": "run_abc123",
"seedUrl": "https://docs.example.com",
"pagesDiscovered": 12,
"documents": [
{
"documentId": "doc_123",
"filename": "docs.example.com-index.md",
"status": "uploaded",
"sourceType": "crawl",
"sourceUrl": "https://docs.example.com/"
}
],
"crawlSourceId": "crawl_456",
"message": "Crawled 12 pages from docs.example.com."
} 202 Queued
The crawl was handed to the background worker. Poll GET …/crawl-runs with the runId to follow it.
{
"projectId": "proj_abc",
"companyId": "co_xyz",
"runId": "run_abc123",
"seedUrl": "https://docs.example.com/",
"message": "Crawl queued for docs.example.com. Pages will appear shortly."
} Status codes
| Status | Meaning |
|---|---|
| 200 | Success. |
| 202 | Queued for the crawl worker. The body carries runId — poll GET …/crawl-runs for progress. |
| 400 | The request body failed validation. |
| 401 | Missing or expired JWT. |
| 402 | The workspace is at its plan's storage cap. Delete documents or upgrade. See plan limits. |
| 403 | The signed-in user's role does not allow this. |
| 404 | No such resource in this workspace. |
| 429 | Rate limited. See rate limits. |
| 502 | Failed to queue crawl job — the enqueue to the worker failed. |
| 503 | RAG is not enabled in this environment. |
curl
curl -X POST 'https://api.oprag.ai/v1/projects/{projectId}/documents/crawl' \
-H 'Authorization: Bearer <Cognito JWT>' \
-H 'Content-Type: application/json' \
-d '{"url": "https://docs.example.com","maxPages": 50,"autoSync": false,"syncIntervalHours": 24}' /v1/projects/{projectId}/crawl-runs Recent crawl runs for a project, newest first — powers the dashboard's live crawl status and history.
Auth Cognito JWT member
Before you call it
- This is what powers the dashboard's live crawl status and history:
queuedandrunningruns appear immediately, so polling it tracks an in-flight crawl started byPOST …/documents/crawl. triggerismanual(started through the API or dashboard) orscheduled(a crawl source's automatic re-crawl).statusrunsqueued→running→completedorfailed.crawlSourceId, the counters (pagesDiscovered,documentsCreated,documentsUpdated,pagesFailed),error,finishedAt, anddurationMsappear only once known — afailedrun carrieserror, a run stillqueuedhas no counters yet.- Match a row to the crawl that started it with the
runIdfromPOST …/documents/crawl. - Runs are retained for about 90 days, then expire.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
projectId | string | Required | Project the request applies to. Must belong to the calling workspace. |
Query parameters
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
limit | number | Optional | 20 | Most recent runs to return, newest first. Values above 50 are clamped to 50; a non-numeric or non-positive value falls back to 20. |
Body parameters
None.GET requests carry no body.
Response
200 Success
{
"runs": [
{
"runId": "run_abc123",
"seedUrl": "https://docs.example.com",
"trigger": "manual",
"status": "completed",
"crawlSourceId": "crawl_456",
"pagesDiscovered": 12,
"documentsCreated": 10,
"documentsUpdated": 2,
"pagesFailed": 0,
"startedAt": "2026-08-27T09:00:00.000Z",
"finishedAt": "2026-08-27T09:01:24.000Z",
"durationMs": 84000
},
{
"runId": "run_def456",
"seedUrl": "https://docs.example.com",
"trigger": "scheduled",
"status": "failed",
"crawlSourceId": "crawl_456",
"pagesDiscovered": 0,
"pagesFailed": 1,
"error": "Seed URL returned 403 Forbidden",
"startedAt": "2026-08-26T09:00:00.000Z",
"finishedAt": "2026-08-26T09:00:05.000Z",
"durationMs": 5000
}
]
} Status codes
curl
curl -X GET 'https://api.oprag.ai/v1/projects/{projectId}/crawl-runs' \
-H 'Authorization: Bearer <Cognito JWT>' /v1/projects/{projectId}/documents/crawl-sources List scheduled website crawl sources
Auth Cognito JWT member
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
projectId | string | Required | Project the request applies to. Must belong to the calling workspace. |
Query parameters
None.Neither paginated nor filtered from the query string.
Body parameters
None.GET requests carry no body.
Response
200 Success
{
"crawlSources": [
{
"crawlSourceId": "crawl_456",
"seedUrl": "https://docs.example.com",
"maxPages": 50,
"syncIntervalHours": 24,
"enabled": true,
"lastCrawledAt": "2026-06-27T12:00:00.000Z",
"nextCrawlAt": "2026-06-28T12:00:00.000Z",
"lastPageCount": 12,
"createdAt": "2026-06-27T12:00:00.000Z",
"updatedAt": "2026-06-27T12:00:00.000Z"
}
]
} Status codes
curl
curl -X GET 'https://api.oprag.ai/v1/projects/{projectId}/documents/crawl-sources' \
-H 'Authorization: Bearer <Cognito JWT>' /v1/projects/{projectId}/documents/crawl-sources/{crawlSourceId} Disable a scheduled crawl source (does not delete crawled documents)
Auth Cognito JWT member
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
projectId | string | Required | Project the request applies to. Must belong to the calling workspace. |
crawlSourceId | string | Required | Crawl source attached to the project. |
Query parameters
None.Acts on the resource named in the path; there is nothing to choose.
Body parameters
None.DELETE requests carry no body.
Response
200 Success
{
"crawlSourceId": "crawl_456",
"enabled": false
} Status codes
curl
curl -X DELETE 'https://api.oprag.ai/v1/projects/{projectId}/documents/crawl-sources/{crawlSourceId}' \
-H 'Authorization: Bearer <Cognito JWT>' /v1/projects/{projectId}/connector-sources Attach a connector source to a project and run initial inline sync (max 50 documents). Documents are uploaded; call documents/sync to ingest.
Auth Cognito JWT admin
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
projectId | string | Required | Project the request applies to. Must belong to the calling workspace. |
Query parameters
None.Everything it needs is in the request body.
Body parameters
| Name | Type | Required | Description |
|---|---|---|---|
connectionId | string | Required | An existing connector connection in this workspace. |
provider | "notion" | "gdrive" | "github" | Required | Which provider the connection belongs to. |
config | any | Optional | Provider-specific selection, e.g. which Notion database or Drive folder. |
Request
{
"connectionId": "conn_abc123",
"provider": "github",
"config": { "owner": "acme", "repo": "docs", "path": "guides" }
} Response
200 Success
{
"connectorSource": {
"connectorSourceId": "csrc_xyz",
"projectId": "proj_abc",
"connectionId": "conn_abc123",
"provider": "github",
"config": { "owner": "acme", "repo": "docs", "path": "guides" },
"status": "idle",
"documentCount": 3,
"lastSyncAt": "2026-06-28T12:10:00.000Z",
"createdAt": "2026-06-28T12:10:00.000Z",
"updatedAt": "2026-06-28T12:10:00.000Z"
},
"documentCount": 3,
"message": "Synced 3 documents from github. Run Sync documents to index."
} Status codes
| Status | Meaning |
|---|---|
| 200 | Success. |
| 400 | The request body failed validation. |
| 401 | Missing or expired JWT. |
| 402 | The workspace is at its plan's storage cap, so the initial sync cannot ingest. See plan limits. |
| 403 | The signed-in user's role does not allow this. |
| 404 | No such resource in this workspace. |
| 429 | Rate limited. See rate limits. |
curl
curl -X POST 'https://api.oprag.ai/v1/projects/{projectId}/connector-sources' \
-H 'Authorization: Bearer <Cognito JWT>' \
-H 'Content-Type: application/json' \
-d '{"connectionId": "conn_abc123","provider": "github","config": { "owner": "acme", "repo": "docs", "path": "guides" }}' /v1/projects/{projectId}/connector-sources List connector sources configured for a project
Auth Cognito JWT admin
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
projectId | string | Required | Project the request applies to. Must belong to the calling workspace. |
Query parameters
None.Neither paginated nor filtered from the query string.
Body parameters
None.GET requests carry no body.
Response
200 Success
{
"connectorSources": [
{
"connectorSourceId": "csrc_xyz",
"projectId": "proj_abc",
"connectionId": "conn_abc123",
"provider": "github",
"config": { "owner": "acme", "repo": "docs", "path": "" },
"status": "idle",
"documentCount": 3,
"createdAt": "2026-06-28T12:10:00.000Z",
"updatedAt": "2026-06-28T12:10:00.000Z"
}
]
} Status codes
curl
curl -X GET 'https://api.oprag.ai/v1/projects/{projectId}/connector-sources' \
-H 'Authorization: Bearer <Cognito JWT>' /v1/projects/{projectId}/connector-sources/{connectorSourceId}/sync Re-sync documents from the external connector (max 50 per run)
Auth Cognito JWT admin
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
projectId | string | Required | Project the request applies to. Must belong to the calling workspace. |
connectorSourceId | string | Required | Connector source attached to the project. |
Query parameters
None.Acts on the resource named in the path; there is nothing to choose.
Body parameters
None.Re-syncs the connector source identified by the path.
Response
200 Success
{
"connectorSource": {
"connectorSourceId": "csrc_xyz",
"status": "idle",
"documentCount": 4,
"lastSyncAt": "2026-06-28T13:00:00.000Z"
},
"documentCount": 4,
"message": "Synced 4 documents. Run Sync documents to index."
} Status codes
| Status | Meaning |
|---|---|
| 200 | Success. |
| 400 | The request body failed validation. |
| 401 | Missing or expired JWT. |
| 402 | The workspace is at its plan's storage cap. Delete documents or upgrade. See plan limits. |
| 403 | The signed-in user's role does not allow this. |
| 404 | No such resource in this workspace. |
| 429 | Rate limited. See rate limits. |
curl
curl -X POST 'https://api.oprag.ai/v1/projects/{projectId}/connector-sources/{connectorSourceId}/sync' \
-H 'Authorization: Bearer <Cognito JWT>' /v1/projects/{projectId}/connector-sources/{connectorSourceId} Remove the connector source record. Previously synced project documents are not deleted in this MVP.
Auth Cognito JWT admin
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
projectId | string | Required | Project the request applies to. Must belong to the calling workspace. |
connectorSourceId | string | Required | Connector source attached to the project. |
Query parameters
None.Acts on the resource named in the path; there is nothing to choose.
Body parameters
None.DELETE requests carry no body.
Response
200 Success
{
"connectorSourceId": "csrc_xyz",
"deleted": true,
"message": "Connector source removed. Previously synced documents were not deleted."
} Status codes
curl
curl -X DELETE 'https://api.oprag.ai/v1/projects/{projectId}/connector-sources/{connectorSourceId}' \
-H 'Authorization: Bearer <Cognito JWT>' Ready to ship?
Get started free