Skip to main content

Prerequisites

1. Upload a document

curl -X POST https://api.okra.app/v1/documents \
  -H "Authorization: Bearer okra_YOUR_KEY" \
  -F file=@invoice.pdf
Response (202):
{
  "id": "doc-abc123",
  "status": "queued"
}
Or upload from a URL:
curl -X POST https://api.okra.app/v1/documents/doc-abc123/upload-url \
  -H "Authorization: Bearer okra_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://example.com/report.pdf"}'

2. Check status

curl https://api.okra.app/v1/documents/doc-abc123 \
  -H "Authorization: Bearer okra_YOUR_KEY"
Response:
{
  "id": "doc-abc123",
  "status": "completed",
  "file_name": "invoice.pdf",
  "total_pages": 3,
  "pages_completed": 3
}

3. Get results

# Full document as markdown
curl https://api.okra.app/v1/documents/doc-abc123/full.md

# Extracted tables
curl https://api.okra.app/v1/documents/doc-abc123/entities/tables

# Single table as CSV
curl "https://api.okra.app/v1/documents/doc-abc123/entities/tables/0?format=csv"

4. Chat with the document

curl -X POST https://api.okrapdf.com/document/doc-abc123/chat/completions \
  -H "Authorization: Bearer okra_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{"messages": [{"role": "user", "content": "What is the total amount?"}]}'

Choose a model

Override the default model with any OpenRouter model ID:
curl -X POST https://api.okrapdf.com/document/doc-abc123/chat/completions \
  -H "Authorization: Bearer okra_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "messages": [{"role": "user", "content": "What is the total amount?"}],
    "agentConfigOverride": {
      "llm": {"chat": {"provider": "openrouter", "model": "anthropic/claude-haiku-4.5"}}
    }
  }'
Or use your own API key to call a provider directly (Anthropic, OpenAI, Google):
curl -X POST https://api.okrapdf.com/document/doc-abc123/chat/completions \
  -H "Authorization: Bearer okra_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -H 'X-Vendor-Keys: {"anthropic": "sk-ant-..."}' \
  -d '{
    "messages": [{"role": "user", "content": "What is the total amount?"}],
    "agentConfigOverride": {
      "llm": {"chat": {"provider": "anthropic", "model": "claude-haiku-4-5-20251001"}}
    }
  }'

Add custom instructions

Append domain context via systemPromptSuffix:
curl -X POST https://api.okrapdf.com/document/doc-abc123/chat/completions \
  -H "Authorization: Bearer okra_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "messages": [{"role": "user", "content": "What is total revenue?"}],
    "systemPromptSuffix": "Amounts are in HKD millions. Respond in one sentence."
  }'

Python example

import requests
import time
import os

API_KEY = os.environ["OKRA_API_KEY"]
BASE = "https://api.okra.app"
headers = {"Authorization": f"Bearer {API_KEY}"}

# Upload
with open("report.pdf", "rb") as f:
    resp = requests.post(f"{BASE}/v1/documents", headers=headers, files={"file": f})
doc_id = resp.json()["id"]

# Poll
while True:
    status = requests.get(f"{BASE}/v1/documents/{doc_id}", headers=headers).json()
    if status["status"] in ("completed", "failed"):
        break
    print(f"Pages: {status.get('pages_completed', 0)}/{status.get('total_pages', '?')}")
    time.sleep(2)

# Get markdown
md = requests.get(f"{BASE}/v1/documents/{doc_id}/full.md").text
print(md)

JavaScript example

const API_KEY = process.env.OKRA_API_KEY;
const BASE = "https://api.okra.app";
const headers = { Authorization: `Bearer ${API_KEY}` };

// Upload from URL
const { id } = await fetch(`${BASE}/v1/documents/${id}/upload-url`, {
  method: "POST",
  headers: { ...headers, "Content-Type": "application/json" },
  body: JSON.stringify({ url: "https://example.com/report.pdf" }),
}).then(r => r.json());

// Poll
let status;
do {
  await new Promise(r => setTimeout(r, 2000));
  status = await fetch(`${BASE}/v1/documents/${id}`, { headers }).then(r => r.json());
} while (!["completed", "failed"].includes(status.status));

// Get tables
const tables = await fetch(`${BASE}/v1/documents/${id}/entities/tables`).then(r => r.json());
console.log(`Extracted ${tables.nodes.length} tables`);