What is a deployment?
A deployment is a permanent URL namespace for interacting with a document or collection. Upload once, deploy once, share the URL.
https://api.okrapdf.com/7km2x9p4ab/completion
The deployment ID is the first path segment — like a Cloudinary cloud name. Everything for that deployment lives under /{id}/....
The object model
Three Durable Objects work together. Each runs on Cloudflare’s edge with its own SQLite database.
Object What it owns Storage DocumentAgent Extracted content (pages, tables, entities), chat history, vendor audit trail SQLite + R2 CollectionAgent Document membership, cross-document queries SQLite + D1 DeploymentAgent Access tokens, guest sessions, redaction policy, frozen manifest SQLite
DocumentAgent and CollectionAgent are internal. They are never exposed to end users directly. All user-facing requests go through a DeploymentAgent, which handles auth, access control, and chat persistence.
Create a deployment
curl -X POST https://api.okrapdf.com/v1/deployments \
-H "Authorization: Bearer $OKRA_API_KEY " \
-H "Content-Type: application/json" \
-d '{
"documentId": "doc-abc123",
"guestAccess": "ask",
"chatPersistence": "persisted"
}'
{
"deploymentId" : "7km2x9p4ab" ,
"completionUrl" : "/7km2x9p4ab/completion" ,
"config" : {
"documentId" : "doc-abc123" ,
"guestAccess" : "ask" ,
"chatPersistence" : "persisted" ,
"dataSource" : "live"
},
"status" : "initialized"
}
Deployment IDs are 10-character base36 strings that always start with a digit. This guarantees they never collide with named routes like /v1/... or /document/....
URL namespace
Every deployment endpoint lives under /{deploymentId}/:
Endpoint Method Description /{id}/completionPOST Ask a question, get an answer /{id}/statusGET Deployment config and guest count /{id}/tokensPOST/GET Create or list access tokens /{id}/tokens/{hint}DELETE Revoke a token /{id}/eventsGET Replay persisted chat events /{id}/document-statusGET Underlying document processing status /{id}/manifestGET Frozen document manifest (collections) /{id}/pg_1.pngGET Page 1 image /{id}/pg_2.mdGET Page 2 markdown /{id}/pages/1/image.pngGET Page image (legacy compat)
Page resources
Clients only need the deployment ID to access page images and markdown. No document ID required.
# Page image (immutable, CDN-cached)
curl https://api.okrapdf.com/7km2x9p4ab/pg_1.png -o page1.png
# Page markdown
curl https://api.okrapdf.com/7km2x9p4ab/pg_3.md
# With shimmer placeholder fallback
curl https://api.okrapdf.com/7km2x9p4ab/d_shimmer/pg_1.png
Images are served from R2 with Cache-Control: public, max-age=31536000, immutable. First request goes through the DO; CDN caches it after that.
Access control
Deployments have three guest access levels:
Level Can view status Can chat Default noneNo No Yes readYes No askYes Yes
Owner access (via API key) always has full permissions.
Access tokens
Create scoped tokens for guests:
# Create a guest token (5 uses, 1 hour)
curl -X POST https://api.okrapdf.com/7km2x9p4ab/tokens \
-H "Authorization: Bearer $OKRA_API_KEY " \
-H "Content-Type: application/json" \
-d '{"role": "ask", "maxUses": 5, "expiresInMs": 3600000}'
{
"token" : "abc123..." ,
"tokenHint" : "abc123..." ,
"role" : "ask" ,
"expiresAt" : 1709312400000
}
Guests use the token as a Bearer token:
curl -X POST https://api.okrapdf.com/7km2x9p4ab/completion \
-H "Authorization: Bearer abc123..." \
-H "Content-Type: application/json" \
-d '{"prompt": "What is total revenue?", "guestId": "user-42"}'
Revoke a token and it stops working instantly:
curl -X DELETE https://api.okrapdf.com/7km2x9p4ab/tokens/abc123 \
-H "Authorization: Bearer $OKRA_API_KEY "
Chat persistence
Mode Behavior ephemeralMessages live only during the WebSocket connection persistedMessages stored in SQLite, replayable via /events
With persisted chat, each guest gets isolated conversation history (scoped by guestId). Replay events with:
curl https://api.okrapdf.com/7km2x9p4ab/events?guestId=user-42 \
-H "Authorization: Bearer $OKRA_API_KEY "
Collection deployments
Deploy over multiple documents by passing a collectionId instead of documentId:
curl -X POST https://api.okrapdf.com/v1/deployments \
-H "Authorization: Bearer $OKRA_API_KEY " \
-H "Content-Type: application/json" \
-d '{
"collectionId": "col-q4-earnings",
"guestAccess": "ask"
}'
Collection deployments freeze a manifest at creation time. Queries fan out to all documents in the manifest and return aggregated results.
curl -X POST https://api.okrapdf.com/9abc123def/completion \
-H "Authorization: Bearer $OKRA_API_KEY " \
-H "Content-Type: application/json" \
-d '{"prompt": "Compare revenue across all filings"}'
{
"results" : [
{ "docId" : "doc-nvidia" , "answer" : "Revenue was $26.9B..." },
{ "docId" : "doc-amd" , "answer" : "Revenue was $5.6B..." }
],
"completed" : 2 ,
"failed" : 0 ,
"totalCost" : 0.0042
}
Redaction
Set redactionRole at deployment creation to control PII visibility:
curl -X POST https://api.okrapdf.com/v1/deployments \
-H "Authorization: Bearer $OKRA_API_KEY " \
-H "Content-Type: application/json" \
-d '{
"documentId": "doc-abc123",
"guestAccess": "ask",
"redactionRole": "viewer"
}'
Role Behavior adminNo redaction (default) viewerPII masked per document’s redaction config publicStrictest masking
The deployment decides the role (who’s looking), the document enforces it (what to mask). See Redaction for configuring what gets masked.
Data flow
Next steps
Public Completion Tokenless public endpoints via share links
Collections Group documents for multi-doc analysis
Redaction Configure PII masking policies
SDK Reference TypeScript SDK for programmatic access