Overview
The oprag API powers document-backed AI chatbots for your product. Upload knowledge in the dashboard, test responses with cited sources, then deploy to receive an API key for production use.
There are two surfaces: a public chat API authenticated with project API keys
(sk_live_...), and a dashboard API authenticated with Cognito JWTs
for managing companies, projects, documents, deployments, and keys.
Retrieval is multi-tenant: each project's documents are isolated by tenant_id and
project_id in the shared Bedrock knowledge base per environment.
Base URLs
All requests use HTTPS. Pick the host for your environment:
https://api.dev.oprag.ai https://api.stg.oprag.ai https://api.oprag.ai
This page is deployed to stg. Example requests below use
https://api.stg.oprag.ai.
Authentication
Dashboard routes
All routes under /v1/* except POST /v1/chat and
GET /v1/widget/{id}/config require a Cognito JWT from the oprag dashboard.
This includes workspace summaries (/v1/dashboard/*, /v1/activity,
/v1/analytics/*, /v1/billing/*, /v1/conversations/*,
/v1/keys), companies, and projects:
Authorization: Bearer <Cognito JWT>
Users must have custom:tenant_id (your companyId) set after company creation.
Only owners can invite another user with role: "owner". Inviting a user who already belongs
to another company returns 409 Conflict.
Public chat
POST /v1/chat accepts your project API key in either header:
X-Oprag-Key: sk_live_...
# or
Authorization: Bearer sk_live_... API keys are issued when you deploy a project from the dashboard.
Roles
Dashboard routes enforce company membership roles. Public POST /v1/chat uses API keys only.
| Role | Access |
|---|---|
member | Read projects and workspace summaries, upload/sync/delete documents, dashboard test chat, conversation inbox, test runs |
admin | Everything members can do, plus project settings, deploy, API keys, team management, widget config updates, archive project |
owner | Same as admin; only owners can invite another user with role: "owner" |
Public Chat API
Ask questions against your project's indexed documents. Answers include cited source filenames.
/v1/chat Auth: X-Oprag-Key or Authorization: Bearer sk_live_...
Request body
{
"question": "What is your refund policy?",
"sessionId": "optional-session-id",
"conversationId": "optional-conversation-id",
"visitorId": "optional-visitor-id"
} | Field | Type | Required | Description |
|---|---|---|---|
question | string | Yes | The user's question. Must be non-empty after trimming and within the project question max length (default 1 000 characters). |
sessionId | string | No | Pass a prior sessionId to continue a multi-turn conversation. |
conversationId | string | No | Resume a persisted conversation from a prior response. |
visitorId | string | No | Stable visitor identifier for public embed analytics and conversation grouping. |
Response
{
"answer": "You can cancel any time from Account → Billing...",
"sessionId": "sess_abc123",
"conversationId": "conv_abc123",
"visitorId": "visitor_abc123",
"sources": [
{
"title": "a1b2c3d4-billing-faq.pdf",
"excerpt": "Refunds are available within 30 days of purchase...",
"pageNumber": 2
}
]
} | Field | Type | Description |
|---|---|---|
answer | string | Generated answer grounded in your documents. |
sessionId | string | Session identifier for follow-up questions. |
conversationId | string | Persisted conversation id when chat history is stored. |
visitorId | string | Echoed visitor id when provided in the request. |
sources | object[] | Cited sources with optional title, excerpt, and pageNumber. Omitted when no citations are returned. |
Example
curl -X POST 'https://api.stg.oprag.ai/v1/chat' \
-H 'X-Oprag-Key: sk_live_...' \
-H 'Content-Type: application/json' \
-d '{"question":"What is your refund policy?","sessionId":"optional-session-id","conversationId":"optional-conversation-id","visitorId":"optional-visitor-id"}' CORS & allowed origins
API Gateway allows browser CORS from any origin at the edge. Lambda enforces per-project
allowedOrigins on POST /v1/chat:
- Empty list — server-to-server calls without an
Originheader are allowed; browser embeds are denied (403). - Non-empty list — browser requests must send an
Originthat exactly matches an entry (scheme and host; trailing slashes matter). Server-to-server calls withoutOriginare still allowed.
Set allowedOrigins on project create or update before embedding the chat widget on customer sites.
Getting an API key
- Upload documents to your project in the dashboard and sync (or deploy — deploy also ingests pending uploads).
- Deploy via
POST /v1/projects/{id}/deploy(admin role). Issues a newsk_live_*key, revokes prior active keys, and auto-ingests uploaded documents. - Use the key in your integration. Keys work as soon as deploy completes — you do not
need to wait for
livestatus if documents are still indexing.
Manage keys from the dashboard or API:
GET /v1/projects/{id}/keys— list keys (prefix only, never the full secret)POST /v1/projects/{id}/keys— create an additional live or test keyPOST /v1/projects/{id}/keys/rotate— rotate live key (requires an existing active key)DELETE /v1/projects/{id}/keys/{keyId}— revoke a keyGET /v1/projects/{id}/integration— endpoint URL and curl exampleGET /v1/keys— list all keys across projects (workspace-level)
API routes
Full route reference. Dashboard routes require Authorization: Bearer <Cognito JWT>;
POST /v1/chat, GET /health, GET /config,
GET /widget.js, and GET /v1/widget/{id}/config are public.
RAG-dependent routes return 503 when the knowledge base is not enabled in an environment.
Route list is defined in site/src/data/api-docs.ts — keep in sync with
backend/src/index.ts.
| Method | Path | Auth | Description |
|---|---|---|---|
| GET | /health | Public | Liveness check |
| GET | /config | Public | Cognito pool/client IDs and API URL (MCP and tooling) |
| GET | /widget.js | Public | Embed widget bundle (301 to CDN when configured) |
| GET | /v1/widget/{id}/config | Public | Widget display config (project must be live or indexing, widget enabled) |
| POST | /v1/chat | API key | Public project chatbot — question max length enforced per project (default 1 000 chars) |
| GET | /v1/dashboard/summary | JWT · member | Dashboard KPIs and project rollups |
| GET | /v1/activity | JWT · member | Recent workspace activity feed |
| GET | /v1/analytics/summary | JWT · member | Usage analytics (optional projectId, from, to query params) |
| GET | /v1/billing/summary | JWT · member | Plan limits and usage |
| GET | /v1/conversations | JWT · member | Workspace conversation inbox |
| GET | /v1/conversations/{conversationId} | JWT · member | Workspace conversation detail and messages |
| PATCH | /v1/conversations/{conversationId} | JWT · member | Update review status, rating, and notes |
| GET | /v1/keys | JWT · member | All API keys across projects |
| POST | /v1/companies | JWT | Create company (signup) |
| GET | /v1/companies/me | JWT · member | Current company |
| PATCH | /v1/companies/me | JWT · admin | Update company name |
| GET | /v1/companies/users | JWT · member | List team members |
| POST | /v1/companies/users/invite | JWT · admin | Invite user |
| POST | /v1/companies/users/{userId}/resend-invite | JWT · admin | Resend invite email |
| PATCH | /v1/companies/users/{userId} | JWT · admin | Change member role |
| DELETE | /v1/companies/users/{userId} | JWT · admin | Remove member |
| GET | /v1/companies/settings | JWT · member | Workspace defaults (origins, prompts, retention) |
| PATCH | /v1/companies/settings | JWT · admin | Update workspace defaults |
| POST | /v1/projects | JWT · member | Create project (settings and allowed origins require admin) |
| GET | /v1/projects | JWT · member | List projects |
| GET | /v1/projects/{id} | JWT · member | Get project |
| PATCH | /v1/projects/{id} | JWT · admin | Update project name, description, CORS origins, system prompt, model, question cap, retrieval and indexing settings |
| DELETE | /v1/projects/{id} | JWT · admin | Archive project |
| GET | /v1/projects/{id}/overview | JWT · member | Project dashboard summary and setup progress |
| GET | /v1/projects/{id}/deploy-readiness | JWT · member | Pre-deploy checklist and blockers |
| GET | /v1/projects/{id}/widget-config | JWT · member | Widget settings (dashboard) |
| PATCH | /v1/projects/{id}/widget-config | JWT · admin | Update widget display name, welcome message, and theme |
| GET | /v1/projects/{id}/test-prompts | JWT · member | List suggested and saved prompt tests |
| POST | /v1/projects/{id}/test-prompts | JWT · admin | Create saved prompt test; prompt max length enforced per project |
| POST | /v1/projects/{id}/test-runs | JWT · member | Run saved or ad hoc prompt tests using project chat settings |
| POST | /v1/projects/{id}/documents/upload-url | JWT · member | Presigned upload URL (single file) |
| POST | /v1/projects/{id}/documents/bulk-upload-url | JWT · member | Presigned upload URLs (batch) |
| POST | /v1/projects/{id}/documents/{docId}/confirm | JWT · member | Confirm S3 upload |
| POST | /v1/projects/{id}/documents/{docId}/replace-url | JWT · member | Replace document (versioned) |
| GET | /v1/projects/{id}/documents/{docId}/versions | JWT · member | List document version history |
| GET | /v1/projects/{id}/documents/{docId}/preview-url | JWT · member | Presigned preview URL (5 min TTL, works without RAG) |
| GET | /v1/projects/{id}/documents | JWT · member | List documents |
| DELETE | /v1/projects/{id}/documents/{docId} | JWT · member | Delete document (S3 + KB) |
| POST | /v1/projects/{id}/documents/sync | JWT · member | Ingest uploaded documents (202 Accepted) |
| POST | /v1/projects/{id}/chat | JWT · member | Dashboard test chat — question max length enforced per project (default 1 000 chars) |
| GET | /v1/projects/{id}/conversations | JWT · member | List project conversations |
| GET | /v1/projects/{id}/conversations/{conversationId} | JWT · member | Project conversation detail and messages |
| POST | /v1/projects/{id}/deploy | JWT · admin | Deploy, auto-ingest docs, issue API key |
| GET | /v1/projects/{id}/integration | JWT · admin | Integration info (endpoint, curl) |
| GET | /v1/projects/{id}/keys | JWT · admin | List API keys (prefix only) |
| POST | /v1/projects/{id}/keys | JWT · admin | Create additional API key (live or test environment) |
| POST | /v1/projects/{id}/keys/rotate | JWT · admin | Rotate live API key |
| DELETE | /v1/projects/{id}/keys/{keyId} | JWT · admin | Revoke key |
Document upload flow
Dashboard routes for ingesting files into your project knowledge base (member role):
- Request upload URLs —
POST /v1/projects/{id}/documents/upload-url(single file) orPOST .../documents/bulk-upload-url(batch) - Upload to S3 —
PUTthe file touploadUrlandmetadataBody(verbatim JSON string) tometadataUploadUrl(15-minute TTL). - Confirm upload —
POST /v1/projects/{id}/documents/{docId}/confirm— verifies S3 objects, marksuploaded, returns the document record. - Sync to knowledge base —
POST /v1/projects/{id}/documents/sync— returns 202 Accepted; ingestion runs asynchronously (wait 1–5 minutes before chatting).
To replace an existing file, use POST .../documents/{docId}/replace-url,
then confirm and sync as usual. Version history is available at
GET .../documents/{docId}/versions.
Upload-url request
{
"filename": "billing-faq.pdf",
"contentType": "application/pdf"
} | Field | Required | Description |
|---|---|---|
filename | Yes | Original filename (used in S3 key suffix) |
contentType | No | Defaults to application/octet-stream |
Upload-url response
{
"documentId": "doc_abc123",
"uploadUrl": "https://...",
"metadataUploadUrl": "https://...",
"metadataBody": "{\"metadataAttributes\":{\"tenant_id\":\"...\",\"project_id\":\"...\"}}",
"key": "documents/{companyId}/{projectId}/{uuid}-billing-faq.pdf",
"metadataKey": "documents/.../billing-faq.pdf.metadata.json",
"bucket": "ashutech-dev-oprag-docs-...",
"expiresInSeconds": 900,
"instructions": "1) PUT file to uploadUrl. 2) PUT metadataBody to metadataUploadUrl. 3) POST confirm. 4) POST sync."
}
PUT metadataBody as the raw body to metadataUploadUrl — do not
JSON.stringify it again. S3 keys use {uuid}-{filename}.
Sync response (202 Accepted)
{
"documentCount": 2,
"statuses": ["STARTING"],
"companyId": "...",
"projectId": "...",
"message": "Project-scoped ingestion started. Wait 1–5 minutes before chatting."
} documentCount reflects uploaded/indexed documents only — not abandoned presigns.
POST .../deploy also confirms pending uploads and ingests documents, so a separate sync
before deploy is optional. Searchability may lag a few minutes after sync or deploy.
Error responses
Errors return JSON with an error string and the appropriate HTTP status:
{
"error": "question is required"
} | Status | Meaning | Common causes |
|---|---|---|
400 | Bad Request | Invalid JSON; missing question or filename; question exceeds the project max length; no documents ready for sync; deploy before rotate; S3 objects missing on confirm |
401 | Unauthorized | Missing or invalid JWT / API key |
403 | Forbidden | Origin not allowed for public chat; insufficient project role |
404 | Not Found | Project, document, key not found; unknown route (No route for METHOD path) |
409 | Conflict | User already belongs to another company; user already a company member; user already has a company (signup) |
502 | Bad Gateway | Chat request failed upstream (Bedrock or retrieval error) |
503 | Service Unavailable | RAG / knowledge base not enabled in this environment |
Health
/health Auth: none (public)
Liveness probe — no authentication required. Use for uptime monitoring and deploy smoke tests.
curl 'https://api.stg.oprag.ai/health' Response
{
"status": "ok",
"service": "oprag-api",
"timestamp": "2026-06-09T12:00:00.000Z"
} Ready to ship?
Get started free