API Reference v1
Get API key
Developer docs

Twin API Reference

A REST API for querying your AI twin, managing your knowledge base, and integrating Twin into any workflow.

Base URL
All API requests are made to:
https://api.twin.ai

Quick start

Get a response from your twin in under 2 minutes.

1
Get your API key
Go to Settings → API & webhooks and copy your key. It starts with twin_live_.
2
Make your first request
Ask your twin anything — it draws from your connected knowledge base.
curl
# Ask your twin a question
curl -X POST https://api.twin.ai/v1/twin \
  -H "Authorization: Bearer twin_live_..." \
  -H "Content-Type: application/json" \
  -d '{"message": "What is the expense policy?"}'
Response
{
  "response":       "Under $50 no pre-approval needed. $50–$500 requires manager email approval before purchase. Submit via Expensify by the 20th.",
  "confidence":     0.92,
  "shouldEscalate": false,
  "sources":        ["Expense reimbursement policy"]
}

Authentication

All API requests must include your API key in the Authorization header.

Header
Authorization: Bearer twin_live_sk_a8f2c4e1...
⚠️
Keep your API key secret. Never include it in client-side code or commit it to a public repo. Use environment variables instead.
ℹ️
API keys can be generated and revoked from Settings → API & webhooks. You can have multiple keys (e.g. one per integration).

Errors

Twin uses standard HTTP status codes. All errors return a JSON body with an error field.

Error response
{
  "error": "Message too long (max 2000 chars)."
}
200
OK
Request succeeded.
201
Created
Resource created successfully.
400
Bad Request
Missing or invalid parameters. Check the error field for details.
401
Unauthorized
Invalid or missing API key.
402
Payment Required
Monthly answer limit reached. Upgrade your plan.
403
Forbidden
Feature not available on your current plan.
422
Unprocessable
Twin not configured. Complete onboarding first.
429
Too Many Requests
Rate limit exceeded. Check Retry-After header.
502
Bad Gateway
Upstream AI provider error. Retry with backoff.

Rate limits

Rate limits are per API key, per minute. Headers indicate your current status.

60
Requests / min
All endpoints
30
KB writes / min
POST /v1/kb
500
Answers / month
Starter plan
Rate limit headers
X-RateLimit-Limit:     60
X-RateLimit-Remaining: 42
X-RateLimit-Reset:     1710423600
Retry-After:           30   # only present on 429 responses

Twin

Ask your twin a question. It searches your knowledge base, generates an answer, and tells you how confident it is.

POST /v1/twin Ask your twin a question
Request body
ParameterTypeRequiredDescription
messagestringrequiredThe question to ask your twin. Max 2,000 characters.
contextstringoptionalAdditional context to include alongside the KB (e.g. thread history). Max 4,000 chars.
askedBystringoptionalName of the person asking. Logged with escalations. Default: "API".
Example request
curl
curl -X POST https://api.twin.ai/v1/twin \
  -H "Authorization: Bearer twin_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "message":  "What is the expense policy for a $200 SaaS tool?",
    "askedBy":  "Jordan M."
  }'
Response
{
  "response":       "Under $50 no pre-approval needed. $50–$500 requires manager email before purchase. Submit via Expensify by the 20th.",
  "confidence":     0.92,
  "shouldEscalate": false,
  "escalateReason": null,
  "sources":        ["Expense reimbursement policy"]
}
{
  "response":       "This requires a personal decision — escalating to AK.",
  "confidence":     0.21,
  "shouldEscalate": true,
  "escalateReason": "Requires personal budget authority over $10k",
  "sources":        []
}
HTTP/1.1 402 Payment Required

{
  "error": "Monthly answer limit reached (500/500). Upgrade to Pro for 2,000 answers/month."
}
Response fields
FieldTypeDescription
responsestringThe twin's answer in plain text.
confidencefloat0.0–1.0. How well the KB supported the answer. Below your threshold → shouldEscalate is true.
shouldEscalatebooleanTrue if the confidence is below your threshold or if the question requires personal judgment.
escalateReasonstring | nullHuman-readable reason for escalation. Null if shouldEscalate is false.
sourcesstring[]Titles of KB documents that contributed to the answer.

Knowledge Base

Create, list, and delete documents in your twin's knowledge base. Supports bulk imports and programmatic sync from external sources.

GET /v1/kb List all KB documents
Query parameters
ParameterTypeDescription
typestringFilter by type: sop, policy, email, slack, drive, note
limitintegerMax results to return. Default 50, max 200.
Example
curl
curl https://api.twin.ai/v1/kb?type=policy \
  -H "Authorization: Bearer twin_live_..."
Response
{
  "docs": [
    {
      "id":        "doc_abc123",
      "title":     "Expense reimbursement policy",
      "type":      "policy",
      "source":    "manual",
      "hits":      8,
      "createdAt": "2026-03-01T10:00:00Z"
    }
  ],
  "total": 1
}
POST /v1/kb Add a document
💡
Use this to bulk-import docs from Notion, Confluence, Google Drive, or any other source. Documents are available to your twin immediately after creation.
Request body
ParameterTypeRequiredDescription
titlestringrequiredDocument title. Shown in source citations.
contentstringrequiredThe document content. Plain text. Max 50,000 characters.
typestringoptionalsop | policy | email | slack | drive | note. Default: sop.
Bulk import example (JavaScript)
JavaScript
// Import all pages from your Notion workspace
const pages = await getNotionPages()

for (const page of pages) {
  await fetch('https://api.twin.ai/v1/kb', {
    method:  'POST',
    headers: {
      'Authorization': `Bearer ${API_KEY}`,
      'Content-Type':  'application/json',
    },
    body: JSON.stringify({
      title:   page.title,
      type:    'note',
      content: page.plainText,
    }),
  })
}
GET /v1/kb/:id Get a single document
Example
curl
curl https://api.twin.ai/v1/kb/doc_abc123 \
  -H "Authorization: Bearer twin_live_..."
Response
{
  "doc": {
    "id":        "doc_abc123",
    "title":     "Expense reimbursement policy",
    "type":      "policy",
    "content":   "Under $50 no pre-approval needed...",
    "source":    "manual",
    "hits":      8,
    "createdAt": "2026-03-01T10:00:00Z"
  }
}
DELETE /v1/kb/:id Delete a document
⚠️
Deletion is permanent. Your twin will stop drawing from this document immediately.
curl
curl -X DELETE https://api.twin.ai/v1/kb/doc_abc123 \
  -H "Authorization: Bearer twin_live_..."
Response
HTTP/1.1 200 OK
{ "deleted": true }

Escalations

List and resolve escalations — questions your twin couldn't answer confidently.

GET /v1/escalations List escalations
Query parameters
ParameterTypeDescription
statusstringopen | resolved | all. Default: open.
limitintegerMax results. Default 50, max 200.
Response
{
  "escalations": [
    {
      "id":         "esc_xyz789",
      "question":   "Can you approve the $15k contract with Acme?",
      "askedBy":   "Jordan M.",
      "confidence": 0.22,
      "reason":     "Requires personal budget authority over $10k",
      "status":     "open",
      "createdAt":  "2026-03-14T10:32:00Z",
      "resolvedAt": null
    }
  ],
  "total": 1
}
PATCH /v1/escalations/:id Resolve an escalation
curl
curl -X PATCH https://api.twin.ai/v1/escalations/esc_xyz789 \
  -H "Authorization: Bearer twin_live_..." \
  -H "Content-Type: application/json" \
  -d '{"status": "resolved"}'

Stats

Usage statistics and performance metrics for your twin.

GET /v1/stats Get usage stats
Response
{
  "totalAnswered":     284,
  "totalEscalated":    34,
  "avgConfidence":     0.81,
  "handleRate":        0.88,
  "totalSavedMinutes": 852,
  "kbDocCount":        12
}

Webhooks

Receive real-time notifications when your twin escalates a question. Set your webhook URL in Settings → API & webhooks.

EVENT escalation.created Fired when twin escalates

Twin sends a POST request to your webhook URL with this body whenever shouldEscalate is true.

Payload
{
  "event":        "escalation.created",
  "escalationId": "esc_xyz789",
  "question":     "Can you approve the $15k contract with Acme?",
  "askedBy":     "Jordan M.",
  "confidence":  0.22,
  "reason":      "Requires personal budget authority over $10k",
  "timestamp":   "2026-03-14T10:32:00Z"
}
Example use cases
📋 Create a Jira/Linear ticket for every escalation
📱 Send an SMS via Twilio when a high-priority escalation comes in
📊 Log to a Google Sheet for tracking patterns over time
🔔 Post to a different Slack channel (#escalations)
SECURITY Webhook verification Validate incoming requests

Every webhook request includes a signature header. Verify it to ensure the request is from Twin.

Header
Twin-Signature: sha256=abc123...
Node.js verification
const crypto = require('crypto')

function verifyWebhook(body, signature, secret) {
  const expected = crypto
    .createHmac('sha256', secret)
    .update(body)
    .digest('hex')

  return crypto.timingSafeEqual(
    Buffer.from(`sha256=${expected}`),
    Buffer.from(signature)
  )
}