Documentation menu
API Reference
Complete reference for all Anansi API endpoints.
Authentication
All requests require a Bearer token in the Authorization header. Create keys from your developer portal.
Authorization: Bearer ans_your_api_key_here
Keys are HMAC-hashed at rest. Every key has a label and creation timestamp visible in the portal. Revoke any key instantly from your dashboard.
POST /v1/ingest
POST/v1/ingest
Ingest content into a user's memory. Content is sanitized, chunked, embedded, and queued for synthesis. Returns immediately — synthesis is asynchronous.
Request body (JSON)
| Field | Type | Description | |
|---|---|---|---|
| userId | string | required | Your app's identifier for this user. Max 256 chars. |
| content | string | required | Text to ingest. Max 100 KB. Secrets and API keys are automatically redacted before storage. |
| sourceType | string | optional | conversation · voice · action · agent_summary · onboarding · meeting. Defaults to api_text. Preserved in relevant[].metadata.sourceType. |
| sourceId | string | optional | Idempotency key. Alphanum + :_./–, max 256 chars. |
| sessionId | string | optional | Groups conversation turns by session. Surfaces in relevant[].metadata.sessionId. |
| agentId | string | optional | Identifies the agent that produced this content. Surfaces in relevant[].metadata.agentId. |
| metadata | object | optional | Arbitrary key-value pairs. Recognised fields: title, author, timestamp, actionType, resourceId, resourceType. |
Response 202
202Response
{ "id": "api:ws_id:user_abc:uuid", "queued": true }
Errors
| Status | Meaning |
|---|---|
| 401 | Missing or invalid API key |
| 400 | Missing required fields or invalid sourceType |
| 402 | Monthly ingest quota exceeded |
| 413 | Content exceeds 100 KB |
| 429 | Rate limit exceeded (100 req/min) |
POST /v1/ingest/batch
POST/v1/ingest/batch
Ingest up to 50 items in a single request. Accepts the same fields as
/v1/ingest per item. Ideal for onboarding flows and bulk imports. Synthesis is triggered once for all unique userIds touched. Each item counts as one ingest call against your monthly quota (a 50-item batch = 50 units).Request body (JSON)
| Field | Type | Description | |
|---|---|---|---|
| items | array | required | Array of ingest objects (1–50). Each item supports the same fields as POST /v1/ingest. |
Response 202
202Response
{ "queued": 4, "ids": ["api:ws:user:uuid1", "api:ws:user:uuid2", ...] }
Errors
| Status | Meaning |
|---|---|
| 400 | items is not an array, is empty, or exceeds 50 |
| 402 | Monthly ingest quota exceeded |
| 429 | Rate limit exceeded |
GET /v1/context
GET/v1/context
Retrieve synthesized memory context for a user. Returns static facts, dynamic context, and (if
q provided) relevant vector search results.Query parameters
| Param | Type | Description | |
|---|---|---|---|
| userId | string | required | Same userId used during ingest. Max 256 chars. Not required when scope=workspace. |
| q | string | optional | Query for vector search. Returns top relevant chunks (capped at 8). Omit for synthesized profile only. Max 2000 chars. |
| scope | string | optional | user (default) or workspace for the team-wide profile across all users. Workspace scope is Pro+. |
| alpha | number | optional | Hybrid weighting: 1.0 = pure vector, 0.0 = pure keyword. Omit for RRF merge. Values other than 1.0 require hybrid search (Pro+). |
| threshold | number | optional | Minimum similarity score (0.0–1.0) for returned chunks. |
| filters | string | optional | JSON-encoded metadata filter (e.g. {"team":"eng"}). Pro+. |
| sessionId | string | optional | Restrict retrieval to a single conversation/session. |
| asOf | ISO 8601 | optional | Point-in-time query. Returns the synthesized profile as it was at this UTC timestamp. Only chunks ingested on or before this date are included. Pro+. |
| asOfKnowledge | ISO 8601 | optional | Bi-temporal query. Returns what your system knew at this timestamp — useful for replaying past system state regardless of when events actually occurred. Pro+. |
Response 200
200Response
{
"static": ["Prefers TypeScript", "Works on payments team"],
"dynamic": ["Building webhook retry system"],
"relevant": [{
"content": "User prefers TypeScript...",
"similarity": 0.87,
"metadata": { "timestamp": "2026-06-08T..." }
}]
}
Errors
| Status | Meaning |
|---|---|
| 401 | Missing or invalid API key |
| 400 | Missing userId or userId exceeds 256 chars |
| 402 | Monthly context quota exceeded |
Tip
Performance: Synthesized profiles are cached in Redis with a 60-second TTL, so repeated context calls within that window skip retrieval entirely. A cache miss runs the profile load plus (when
q is set) one hybrid search query.GET /v1/entities
GET/v1/entities
Return the entity graph for a user — people, organizations, and tools extracted from ingested content. Updated after each synthesis pass.
Query parameters
| Param | Type | Description | |
|---|---|---|---|
| userId | string | required | The userId to retrieve the entity graph for. |
| type | string | optional | Filter by entity type: person · organization · tool. Omit to return all types. |
Response 200
200Response
{
"entities": [
{ "name": "Sarah", "type": "person", "relationship": "manager", "confidence": 0.91 },
{ "name": "Stripe", "type": "organization", "relationship": "employer", "confidence": 0.87 },
{ "name": "BullMQ", "type": "tool", "relationship": "uses", "confidence": 0.94 }
]
}
Errors
| Status | Meaning |
|---|---|
| 401 | Missing or invalid API key |
| 400 | Missing userId |
| 404 | No entity graph found for this user (not yet synthesized) |
POST /v1/search
POST/v1/search
Hybrid vector + keyword search across all memory chunks for a user. Send a JSON body. Use this for raw scored retrieval beyond the top-K results returned by
GET /v1/context.Request body (JSON)
| Field | Type | Description | |
|---|---|---|---|
| userId | string | required | The userId to search memory for. Max 256 chars. |
| query | string | required | Search query, max 2000 chars. Combined vector similarity + BM25 keyword match. |
| searchMode | string | optional | semantic · hybrid (default) · keyword. Hybrid/keyword are Pro+. |
| alpha | number | optional | 1.0 = pure vector, 0.0 = pure keyword. Omit for RRF merge (Pro+). |
| threshold | number | optional | Minimum similarity score (0.0–1.0). |
| limit | number | optional | Max results to return (default 8, max 50). |
| filters | object | optional | JSONB metadata filters (Pro+). |
| sourceId | string | optional | Scope search to one ingested document. |
| sessionId | string | optional | Restrict search to a session. |
Response 200
200Response
{
"results": [
{ "content": "User prefers TypeScript...", "similarity": 0.89, "sourceType": "conversation", "ingestedAt": "2026-06-10T..." },
{ "content": "Debugging BullMQ retry logic", "similarity": 0.82, "sourceType": "meeting", "ingestedAt": "2026-06-09T..." }
]
}
DELETE /v1/memory
DELETE/v1/memory
Delete all memory for a user — chunks, embeddings, and synthesized profiles. Idempotent.
Query parameters
| Param | Type | Description | |
|---|---|---|---|
| userId | string | required | The userId whose memory to delete entirely. |
Response 200
200Response
{ "deleted": 7 }
GET /v1/ledger
GET/v1/ledger
Reconstruct the ledger — cited, trust-tiered claims for a workspace — at any point in time. With
asOfKnowledge, answers reflect what was believed at that instant; with asOf, what was true; with neither, the current ledger. Every claim carries its supporting evidence; competing active answers for one question surface as disputes.Query parameters
| Param | Type | Description | |
|---|---|---|---|
| domain | string | optional | Restrict the fold to a single domain. Omit to return the whole ledger. |
| asOf | ISO 8601 | optional | Valid-time coordinate — reconstruct what was true at this instant. Accepts YYYY-MM, YYYY-MM-DD, or full ISO 8601. |
| asOfKnowledge | ISO 8601 | optional | Knowledge-time coordinate — reconstruct what the system believed at this instant. |
Response 200
200Response
{
"workspaceId": "ws_abc",
"domain": null,
"asOf": null,
"asOfKnowledge": null,
"claims": [
{
"claim": "Prod deploys require two approvals",
"claimKey": "deploy.approvals",
"claimFingerprint": "a1b2c3",
"claimType": "policy",
"status": "observed",
"disputed": false,
"confidence": 0.92,
"validFrom": "2026-03-01T00: 00: 00.000Z",
"validFromBasis": "stated",
"evidence": [
{ "chunkId": "chunk-uuid", "quote": "all prod deploys need two approvals", "sourceType": "notion_page" }
],
"recordedAt": "2026-03-02T09: 00: 00.000Z"
}
],
"disputes": []
}
Errors
| Status | Meaning |
|---|---|
| 401 | Missing or invalid API key |
| 400 | Invalid asOf or asOfKnowledge date |
| 429 | Rate limit exceeded (60 req/min) |
GET /v1/ledger/divergences
GET/v1/ledger/divergences
Where a documented answer (wiki, runbook, Notion) disagrees with the observed reality (chat, tickets) for the same question — the doc-vs-reality view only a bi-temporal ledger can produce. Reads history, not just active rows, so it catches docs the ledger already superseded.
Query parameters
| Param | Type | Description | |
|---|---|---|---|
| domain | string | optional | Restrict to a single domain. Omit to scan all domains. |
Response 200
200Response
{
"divergences": [
{
"claimKey": "deploy.approvals",
"documented": {
"claim": "Prod deploys require two approvals",
"fingerprint": "a1b2c3",
"validFrom": "2026-01-01T00: 00: 00.000Z",
"evidence": [{ "chunkId": "c1", "quote": "two approvals", "sourceType": "notion_page" }]
},
"observed": {
"claim": "Prod deploys ship with one approval",
"fingerprint": "d4e5f6",
"validFrom": "2026-04-01T00: 00: 00.000Z",
"evidence": [{ "chunkId": "c2", "quote": "just LGTM and merge", "sourceType": "conversation" }]
},
"changedAt": "2026-04-01T00: 00: 00.000Z"
}
]
}
Errors
| Status | Meaning |
|---|---|
| 401 | Missing or invalid API key |
| 429 | Rate limit exceeded (60 req/min) |
GET /v1/ledger/timeline
GET/v1/ledger/timeline
A chronological record of when each answer was adopted and (if closed) superseded.
adopted uses a stated start date when one exists, otherwise the moment the claim was first recorded — never an invented date. Sorted ascending by time.Query parameters
| Param | Type | Description | |
|---|---|---|---|
| domain | string | optional | Restrict to a single domain. Omit for all domains. |
Response 200
200Response
{
"timeline": [
{ "at": "2026-01-01T00: 00: 00.000Z", "claimKey": "deploy.approvals", "claim": "Prod deploys require two approvals", "fingerprint": "a1b2c3", "kind": "adopted" },
{ "at": "2026-04-01T00: 00: 00.000Z", "claimKey": "deploy.approvals", "claim": "Prod deploys require two approvals", "fingerprint": "a1b2c3", "kind": "superseded" }
]
}
Errors
| Status | Meaning |
|---|---|
| 401 | Missing or invalid API key |
| 429 | Rate limit exceeded (60 req/min) |
Rate limits
| Endpoint | Per-minute | Monthly (API plan) |
|---|---|---|
| POST /v1/ingest | 100 req/min | 10,000/month |
| GET /v1/context | 60 req/min | 5,000/month |
| GET /v1/entities | 60 req/min | Unlimited |
| POST /v1/search | 60 req/min | Counts against context quota |
| DELETE /v1/memory | 60 req/min | Unlimited |
| GET /v1/ledger* | 60 req/min | Unlimited |
Monthly limit exceeded → 402. Per-minute limit exceeded → 429.