Integration API
Every endpoint an integration key, an embed key, or no credential at all can reach — the set you build against.
These are the endpoints you can call from your own product. 13 of them take an sk_live_ integration key, an origin-locked embed_live_ key, or no credential at all. Nothing on this page needs a dashboard login.
Everything your key opens
| Endpoint | Credential | What it does |
|---|---|---|
GET /v1/public/share/{slug} | Public | Bootstrap the hosted share page for a slug and mint a short-lived chat token. |
POST /v1/chat | Integration or embed key | Ask a question against a project's documents and get a grounded answer with citations. |
POST /v1/chat/stream | Integration or embed key | The same question, answered as a Server-Sent Events stream of tokens. |
POST /v1/chat/feedback | Integration key | Record a visitor's thumbs up or down on an assistant message. |
POST /v1/projects/{projectId}/conversations/{conversationId}/escalate | Integration key | Hand a public conversation to a human, over a webhook, an email, or both. |
POST /v1/projects/{projectId}/leads | Integration key | Record a lead captured by the widget's contact form. |
POST /v1/projects/{projectId}/tickets | Integration key | Create an escalation ticket from a conversation. |
GET /widget.js | Public | Embed widget bundle (301 to CDN when configured) |
GET /v1/widget/{projectId}/config | Public | Public display configuration for a project's chat widget. Optional fields — quickPrompts, avatarUrl, launcherIconUrl, ctaChips, proactiveTriggers, and lead-capture settings — appear only when set; visitorContextEnabled is a master consent switch. |
GET /v1/widget/{projectId}/conversations/{conversationId}/messages | Integration or embed key | Restore a returning visitor's transcript, using the conversationId and visitorId the widget saved. |
GET /health | Public | Liveness check |
GET /config | Public | Cognito pool/client IDs, API URL, Hosted UI domain prefix, SPA OAuth callback, and enabled socialProviders (Google, GitHub). Used by the dashboard and MCP clients to bootstrap auth. |
GET /dev/info | Public | Dev testing credentials, MCP login steps, and the live API route catalog. |
Chat
Ask questions against a project's documents, stream answers, and record feedback. The endpoints most integrations start and end with.
/v1/chat Ask a question against a project's documents and get a grounded answer with citations.
Auth Integration keyEmbed keyShare token Embed keys require a browser `Origin`; share tokens use `X-Oprag-Share-Token`
Path parameters
None.
Query parameters
None.Everything it needs is in the request body.
Body parameters
| Name | Type | Required | Description |
|---|---|---|---|
question | string | Required | The visitor's question. Trimmed; an empty question is rejected with 400. |
sessionId | string | Optional | Accepted but ignored on this channel: the public API resolves the model session from the conversation itself. Use conversationId + visitorId for multi-turn context. |
conversationId | string | Optional | Server-assigned conversation identifier. Echo it back so the conversation stays one thread in the dashboard. |
visitorId | string | Optional | Stable identifier for the end user. Ties leads and escalations to the person who asked. |
visitorContext | object | Optional | Referrer/UTM attribution the widget captured with visitor consent (referrer, landingPage, utmSource, utmMedium, utmCampaign, utmTerm, utmContent — every field optional, each capped at 500 characters). Only ever attached on the send that creates the conversation; omitted once one exists. A project owner can turn off this capture and the widget's consent banner entirely with visitorContextEnabled — see GET /v1/widget/{projectId}/config. |
Request
{
"question": "What is your refund policy?",
"conversationId": "conv_abc123",
"visitorId": "visitor_abc123"
} Responses
200 Answer
The usual branch. type is answer and sources carries the passages it was grounded in.
{
"type": "answer",
"answer": "You can cancel any time from Account → Billing. Refunds are available within 30 days of purchase.",
"conversationId": "conv_abc123",
"visitorId": "visitor_abc123",
"assistantMessageId": "msg_abc123",
"sources": [
{
"documentTitle": "Billing FAQ",
"excerpt": "Refunds are available within 30 days of purchase...",
"location": "billing-faq.pdf",
"pageNumber": 2,
"score": 0.89
}
],
"suggestedFollowUps": ["How long do refunds take?", "Can I pause my plan instead?"]
} 200 Lead capture prompt
Returned when smart lead capture is enabled and the documents cannot answer. There is no answer field on this branch — branch on type before reading it.
{
"type": "lead_capture_prompt",
"promptMessage": "I could not find that in the docs. Leave your email and someone will follow up.",
"fields": ["name", "email"],
"conversationId": "conv_abc123",
"visitorId": "visitor_abc123",
"assistantMessageId": "msg_abc123"
} 200 Test key fixture
What an sk_test_ or embed_test_ key returns. It omits type — a legacy-shaped payload that clients treat as the answer branch.
{
"answer": "This is a test response from oprag. Your integration is working correctly. Replace this with a live key to get real answers from your documents.",
"sources": [],
"conversationId": "conv_abc123"
} Status codes
curl
curl -X POST 'https://api.oprag.ai/v1/chat' \
-H 'X-Oprag-Key: sk_live_...' \
-H 'Content-Type: application/json' \
-d '{"question": "What is your refund policy?","conversationId": "conv_abc123","visitorId": "visitor_abc123"}' SDK equivalent `oprag.chat.ask()`
/v1/chat/stream The same question, answered as a Server-Sent Events stream of tokens.
Auth Integration keyEmbed keyShare token Same auth as `POST /v1/chat`
Path parameters
None.
Query parameters
None.Everything it needs is in the request body.
Body parameters
| Name | Type | Required | Description |
|---|---|---|---|
question | string | Required | The visitor's question. Trimmed; an empty question is rejected with 400. |
sessionId | string | Optional | Accepted but ignored on this channel: the public API resolves the model session from the conversation itself. Use conversationId + visitorId for multi-turn context. |
conversationId | string | Optional | Server-assigned conversation identifier. Echo it back so the conversation stays one thread in the dashboard. |
visitorId | string | Optional | Stable identifier for the end user. Ties leads and escalations to the person who asked. |
visitorContext | object | Optional | Referrer/UTM attribution the widget captured with visitor consent (referrer, landingPage, utmSource, utmMedium, utmCampaign, utmTerm, utmContent — every field optional, each capped at 500 characters). Only ever attached on the send that creates the conversation; omitted once one exists. A project owner can turn off this capture and the widget's consent banner entirely with visitorContextEnabled — see GET /v1/widget/{projectId}/config. |
Request
{
"question": "What is your refund policy?",
"conversationId": "conv_abc123",
"visitorId": "visitor_abc123"
} Response
Status codes
curl
curl -X POST 'https://api.oprag.ai/v1/chat/stream' \
-H 'X-Oprag-Key: sk_live_...' \
-H 'Content-Type: application/json' \
-d '{"question": "What is your refund policy?","conversationId": "conv_abc123","visitorId": "visitor_abc123"}' SDK equivalent `oprag.chat.stream()`
/v1/chat/feedback Record a visitor's thumbs up or down on an assistant message.
Auth Integration key
Before you call it
messageIdis theassistantMessageIdfrom the answer being rated.visitorIdbecomes required once the conversation already has one.- Respects the widget's
feedbackEnabledsetting, which defaults to on, and is rate limited per IP.
Path parameters
None.
Query parameters
None.Everything it needs is in the request body.
Body parameters
| Name | Type | Required | Description |
|---|---|---|---|
conversationId | string | Required | Conversation this relates to. |
messageId | string | Required | The assistantMessageId from the answer being rated. |
rating | "up" | "down" | Required | up or down. |
visitorId | string | Optional | Stable identifier for the end user. |
Request
{
"conversationId": "conv_xyz789",
"messageId": "msg_abc123",
"rating": "up",
"visitorId": "visitor_001"
} Response
200 Success
{
"ok": true,
"messageId": "msg_abc123",
"rating": "up",
"ratedAt": "2026-08-11T18:00:00.000Z"
} Status codes
curl
curl -X POST 'https://api.oprag.ai/v1/chat/feedback' \
-H 'X-Oprag-Key: sk_live_...' \
-H 'Content-Type: application/json' \
-d '{"conversationId": "conv_xyz789","messageId": "msg_abc123","rating": "up","visitorId": "visitor_001"}' Conversations
Read conversation history, review it, export it, and escalate a thread to a human.
/v1/projects/{projectId}/conversations/{conversationId}/escalate Hand a public conversation to a human, over a webhook, an email, or both.
Auth Integration key
Before you call it
- The project needs
escalationWebhookUrlorescalationEmailin its settings; without one there is nowhere to escalate to. - On the Growth plan and above the escalation is also stored as a native ticket, and emits
ticket.created. - When a webhook is configured, oprag POSTs the transcript to it over HTTPS with an
X-Oprag-Signature: sha256=…header, includingticketIdwhen one was created. - Email escalation needs
SES_FROM_EMAILconfigured.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
projectId | string | Required | Project the request applies to. Must belong to the calling workspace. |
conversationId | string | Required | Conversation within the project. |
Query parameters
None.Everything it needs is in the request body.
Body parameters
| Name | Type | Required | Description |
|---|---|---|---|
visitorId | string | Required | The visitor asking for a human. Required. |
subject | string | Optional | Short summary line. |
requesterEmail | string | Optional | Email to reply to. |
requesterName | string | Optional | Name of the person asking. |
message | string | Optional | Body text. |
Request
{ "visitorId": "visitor_001", "message": "Need billing help" } Response
200 Success
{ "escalated": true, "ticketId": "tkt_abc123" } Status codes
| Status | Meaning |
|---|---|
| 200 | Escalated. The body is { escalated: true, ticketId? }. |
| 400 | The request body failed validation. |
| 401 | Missing, revoked, or wrong-environment key. |
| 403 | Origin or IP not allowed for this project. |
| 404 | No such resource in this workspace. |
| 409 | This conversation has already been escalated. |
| 429 | Rate limited. See rate limits. |
| 500 | A webhook URL is configured but its signing secret is missing. |
curl
curl -X POST 'https://api.oprag.ai/v1/projects/{projectId}/conversations/{conversationId}/escalate' \
-H 'X-Oprag-Key: sk_live_...' \
-H 'Content-Type: application/json' \
-d '{ "visitorId": "visitor_001", "message": "Need billing help" }' SDK equivalent `oprag.conversations.escalate()`
Leads & tickets
Capture leads from chat, export them, and manage the tickets escalation creates.
/v1/projects/{projectId}/leads Record a lead captured by the widget's contact form.
Auth Integration key
Before you call it
- The project needs
leadCaptureEnabled, and the body needsvisitorIdplus at least one ofnameoremail. - Built-in lead fields are unlimited on every plan — there is no monthly cap.
customFieldsneeds the Growth plan or above withleadFormFieldsconfigured on the project.
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 |
|---|---|---|---|
visitorId | string | Required | Stable identifier for the end user. |
name | string | Optional | Display name. |
email | string | Optional | Email address. |
conversationId | string | Optional | Conversation this relates to. |
sourceQuestion | string | Optional | The question that triggered capture, for context in the dashboard. |
customFields | object | Optional | Values for any custom lead form fields configured on the project. |
Request
{
"visitorId": "visitor_001",
"name": "Jane Doe",
"email": "jane@example.com",
"conversationId": "conv_xyz789",
"customFields": {
"company_size": "50-200",
"plan_tier": "Growth"
}
} Response
200 Success
{ "leadId": "lead_abc123" } Status codes
| Status | Meaning |
|---|---|
| 201 | Created. The body is { leadId }. |
| 400 | The request body failed validation. |
| 401 | Missing, revoked, or wrong-environment key. |
| 402 | customFields were sent on the Free plan. |
| 403 | Origin or IP not allowed for this project. |
| 404 | No such resource in this workspace. |
| 429 | More than 10 submissions from one visitor within an hour. |
curl
curl -X POST 'https://api.oprag.ai/v1/projects/{projectId}/leads' \
-H 'X-Oprag-Key: sk_live_...' \
-H 'Content-Type: application/json' \
-d '{"visitorId": "visitor_001","name": "Jane Doe","email": "jane@example.com","conversationId": "conv_xyz789","customFields": {"company_size": "50-200","plan_tier": "Growth"}}' SDK equivalent `oprag.leads.create()`
/v1/projects/{projectId}/tickets Create an escalation ticket from a conversation.
Auth Integration key
Before you call it
- Delivery is the same as
escalate— webhook, email, or both.
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 |
|---|---|---|---|
conversationId | string | Required | Conversation the ticket is raised from. |
visitorId | string | Optional | Stable identifier for the end user. |
subject | string | Optional | Short summary line. |
requesterEmail | string | Optional | Email to reply to. |
requesterName | string | Optional | Name of the person asking. |
message | string | Optional | Body text. |
Request
{
"conversationId": "conv_abc123",
"visitorId": "visitor_001",
"subject": "Billing question"
} Response
200 Success
{ "ticketId": "tkt_abc123", "escalated": true } Status codes
| Status | Meaning |
|---|---|
| 200 | Free plan — the escalation was delivered but no ticket is stored: { escalated: true }. |
| 201 | Growth and above — a native ticket was stored: { ticketId, escalated: true }. |
| 400 | The request body failed validation. |
| 401 | Missing, revoked, or wrong-environment key. |
| 403 | Origin or IP not allowed for this project. |
| 404 | No such resource in this workspace. |
| 429 | Rate limited. See rate limits. |
curl
curl -X POST 'https://api.oprag.ai/v1/projects/{projectId}/tickets' \
-H 'X-Oprag-Key: sk_live_...' \
-H 'Content-Type: application/json' \
-d '{"conversationId": "conv_abc123","visitorId": "visitor_001","subject": "Billing question"}' Channels
Where chat is exposed: the embeddable widget, its public configuration, and WhatsApp.
/widget.js Embed widget bundle (301 to CDN when configured)
Auth Public — no credential
Path parameters
None.
Query parameters
None.Serves a fixed document and takes no input at all.
Body parameters
None.GET requests carry no body.
Response
Status codes
| Status | Meaning |
|---|---|
| 200 | Success. |
| 429 | Rate limited. See rate limits. |
curl
curl -X GET 'https://api.oprag.ai/widget.js' \
-H 'X-Oprag-Key: sk_live_...' /v1/widget/{projectId}/config Public display configuration for a project's chat widget. Optional fields — quickPrompts, avatarUrl, launcherIconUrl, ctaChips, proactiveTriggers, and lead-capture settings — appear only when set; visitorContextEnabled is a master consent switch.
Auth Public — no credential
Before you call it
- Only served while the project is live or indexing and its widget is enabled.
- Returns
displayName,welcomeMessage, andtheme(primaryColor,position, and an optionalmode). theme.modeisauto,light, ordark.autofollows the visitor'sprefers-color-scheme.quickPromptsand the lead-capture fields appear only when those features are on,avatarUrl,launcherIconUrl,ctaChips, andproactiveTriggersonly when set on the widget config, andescalationEnabledonly when the project has an escalation webhook or email.proactiveTriggersdrive the widget's teaser bubbles. The widget shows at most one teaser per session, and suppresses all teasers for 7 days after a visitor dismisses one.visitorContextEnabledis a project-level master switch, distinct from the widget's per-visitor consent banner (window.oprag.setConsent()/handle.setConsent()). Omitted ortrue(the default) leaves visitor-context capture and the consent banner on;falsedisables both for every visitor on the project.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
projectId | string | Required | Project the request applies to. Must belong to the calling workspace. |
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
200 Success
{
"displayName": "Acme Support",
"welcomeMessage": "Hi! Ask me anything about our product.",
"quickPrompts": ["What is your refund policy?"],
"avatarUrl": "https://cdn.acme.com/bot-avatar.png",
"launcherIconUrl": "https://cdn.acme.com/launcher-icon.svg",
"ctaChips": [
{ "label": "Book a demo", "url": "https://acme.com/demo" },
{ "label": "Email us", "url": "mailto:hello@acme.com" }
],
"proactiveTriggers": [
{
"message": "Questions about pricing? I can help!",
"trigger": "delay",
"value": 30,
"pagePattern": "/pricing*"
}
],
"feedbackEnabled": true,
"visitorContextEnabled": true,
"escalationEnabled": false,
"theme": {
"primaryColor": "#2563eb",
"position": "bottom-right",
"mode": "auto"
}
} Status codes
| Status | Meaning |
|---|---|
| 200 | Success. |
| 404 | No such resource in this workspace. |
| 429 | Rate limited. See rate limits. |
curl
curl -X GET 'https://api.oprag.ai/v1/widget/{projectId}/config' \
-H 'X-Oprag-Key: sk_live_...' SDK equivalent `mountWidget()` reads this
/v1/widget/{projectId}/conversations/{conversationId}/messages Restore a returning visitor's transcript, using the conversationId and visitorId the widget saved.
Auth Embed keyIntegration key Embed keys require a browser `Origin` on the project's allowlist
Before you call it
- This is what powers the widget's transcript restore: the widget persists
conversationIdandvisitorIdlocally and replays the thread when the visitor comes back. messagesis chronological (oldest first), capped at the most recent 50.- Each message carries
role(userorassistant),text, andcreatedAt.sourcesappears only when the stored answer cited documents, and each entry carriesdocumentTitleonly — no excerpts, locations, or scores on this channel. - The 404 is deliberately indistinguishable: a missing conversation, a conversation from another project, and a
visitorIdmismatch all answer identically, so conversation ids cannot be probed.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
projectId | string | Required | Project the request applies to. Must belong to the calling workspace. |
conversationId | string | Required | Conversation within the project. |
Query parameters
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
visitorId | string | Required | — | The visitor the widget saved alongside the conversationId. A missing or blank value is a 400; a mismatch is the same 404 as an unknown conversation. |
Body parameters
None.GET requests carry no body.
Response
200 Success
{
"messages": [
{
"role": "user",
"text": "What is your refund policy?",
"createdAt": "2026-08-27T09:14:03.000Z"
},
{
"role": "assistant",
"text": "Refunds are available within 30 days of purchase.",
"createdAt": "2026-08-27T09:14:07.000Z",
"sources": [{ "documentTitle": "Refund policy" }]
}
]
} Status codes
| Status | Meaning |
|---|---|
| 200 | Success. |
| 400 | visitorId is required — the query parameter is missing or blank. |
| 401 | Missing, revoked, or wrong-environment key. |
| 403 | No Origin header, an origin that is not allowlisted, or a blocked IP. |
| 404 | Conversation not found — an unknown conversation, another project's conversation, or a visitorId that does not match. All three are indistinguishable by design. |
| 429 | Rate limited. See rate limits. |
| 503 | Project is not live yet. Wait for indexing to complete before chatting. |
curl
curl -X GET 'https://api.oprag.ai/v1/widget/{projectId}/conversations/{conversationId}/messages' \
-H 'X-Oprag-Key: sk_live_...' Platform
Health, client bootstrap configuration, and environment information.
/health Liveness check
Auth Public — no credential
Path parameters
None.
Query parameters
None.Serves a fixed document and takes no input at all.
Body parameters
None.GET requests carry no body.
Response
200 Success
{
"status": "ok",
"service": "oprag-api",
"timestamp": "2026-06-09T12:00:00.000Z"
} Status codes
| Status | Meaning |
|---|---|
| 200 | Success. |
| 429 | Rate limited. See rate limits. |
curl
curl -X GET 'https://api.oprag.ai/health' \
-H 'X-Oprag-Key: sk_live_...' /config Cognito pool/client IDs, API URL, Hosted UI domain prefix, SPA OAuth callback, and enabled socialProviders (Google, GitHub). Used by the dashboard and MCP clients to bootstrap auth.
Auth Public — no credential
Path parameters
None.
Query parameters
None.Serves a fixed document and takes no input at all.
Body parameters
None.GET requests carry no body.
Response
200 Success
{
"cognitoUserPoolId": "us-east-1_example",
"cognitoClientId": "exampleclientid",
"apiUrl": "https://api.dev.oprag.ai",
"cognitoRegion": "us-east-1",
"cognitoDomain": "ashutech-dev-oprag",
"oauthRedirectUri": "https://app.dev.oprag.ai/auth/callback",
"socialProviders": ["Google", "GitHub"]
} Status codes
| Status | Meaning |
|---|---|
| 200 | Success. |
| 429 | Rate limited. See rate limits. |
curl
curl -X GET 'https://api.oprag.ai/config' \
-H 'X-Oprag-Key: sk_live_...' /dev/info Dev testing credentials, MCP login steps, and the live API route catalog.
Auth Public — no credential
Before you call it
- Returns 404 in staging and production — it exists to bootstrap local and dev testing only.
- The response carries
mcp.authModes,mcp.notes,mcp.testProject, the fullloginStepsarray, andapis.routes— the live catalog of every main-backend route. - The dev QA password is never returned.
Path parameters
None.
Query parameters
None.Serves a fixed document and takes no input at all.
Body parameters
None.GET requests carry no body.
Response
200 Success
{
"environment": "dev",
"apiUrl": "https://api.dev.oprag.ai",
"docsUrl": "https://dev.oprag.ai/docs/",
"dashboardUrl": "https://app.dev.oprag.ai",
"mcp": {
"credentials": {
"apiUrl": "https://api.dev.oprag.ai",
"cognitoUserPoolId": "us-east-1_example",
"cognitoClientId": "exampleclientid",
"email": "dev-qa@oprag.ai"
},
"testAccount": { "email": "dev-qa@oprag.ai", "companyName": "Dev QA" },
"testProject": { "purposeId": "mcp-qa", "name": "Uolo Product Assistant" },
"authModes": [
{ "mode": "credentials", "priority": 1, "env": ["OPRAG_API_URL", "OPRAG_COGNITO_EMAIL", "OPRAG_COGNITO_PASSWORD"] },
{ "mode": "jwt", "priority": 2, "env": ["OPRAG_API_URL", "OPRAG_JWT_TOKEN"] },
{ "mode": "api-key-only", "priority": 3, "env": ["OPRAG_API_URL", "OPRAG_API_KEY"], "description": "health_check and public_chat only" }
],
"loginSteps": [
{ "step": 1, "title": "Install and build the MCP server", "commands": ["cd mcp", "npm install", "npm run build"] },
{ "step": 2, "title": "Configure credentials", "commands": ["cp mcp/.env.example mcp/.env"] }
],
"notes": ["Password self-signup is disabled; use provisioned dev QA or invite."],
"envExample": { "OPRAG_API_URL": "https://api.dev.oprag.ai", "OPRAG_COGNITO_EMAIL": "dev-qa@oprag.ai" }
},
"apis": {
"count": 74,
"routes": [{ "method": "GET", "path": "/health", "auth": "Public", "group": "health" }]
}
} Status codes
| Status | Meaning |
|---|---|
| 200 | Success. |
| 429 | Rate limited. See rate limits. |
curl
curl -X GET 'https://api.oprag.ai/dev/info' \
-H 'X-Oprag-Key: sk_live_...' Endpoints that call you
A third party calls us and signs the request — Stripe, Meta, Slack. You configure the URL in their console; you never call it yourself.
| Endpoint | Signed by | Full documentation |
|---|---|---|
POST /v1/billing/webhook | Updates the workspace's plan and billing status from Stripe's checkout and subscription events. | Workspace |
GET /v1/webhooks/whatsapp | Answers Meta's hub subscription challenge when the webhook is registered. | Channels |
POST /v1/webhooks/whatsapp | Receives inbound WhatsApp messages from Meta and answers them through the project's chat pipeline. | Channels |
POST /v1/slack/events | Slack Events API callback (url_verification, app_mention, DM) | Integrations |
Endpoints Cognito calls
Exists for Cognito to call during federated sign-in. Not a route you integrate against. They are documented on OAuth & sign-in, which explains where the authorize step actually happens.
Ready to ship?
Get started free