API version v1

API Documentation

Everything you need to add persistent AI agent memory to your automation workflows. Base URL: https://api.retainr.dev

Authentication

All API requests require a Bearer token in the Authorization header. Generate API keys from your dashboard.

Authorization: Bearer rec_live_<your_api_key>

# Example curl (search uses POST with JSON body)
curl -X POST https://api.retainr.dev/v1/memories/search \
  -H "Authorization: Bearer rec_live_abc123..." \
  -H "Content-Type: application/json" \
  -d '{"query":"customer preferences","namespace":"customer:alice","limit":5}'

Key format: API keys start with rec_live_ followed by 32 base58 characters. Store them in environment variables - never hardcode in workflow nodes.

Quick start - 2 API calls to memory

The core loop is: store what your agent learns, search for relevant context before responding. Nothing else required.

1. Store a memory
// POST https://api.retainr.dev/v1/memories
{
  "content": "Customer prefers email over phone calls and is on a budget",
  "namespace": "customer:alice",
  "tags": ["preference", "contact-method"]
}
2. Search before your next AI call
// POST https://api.retainr.dev/v1/memories/search
{
  "query": "how should I contact this customer?",
  "namespace": "customer:alice",
  "limit": 5
}

// Response
{
  "results": [
    {
      "id": "mem_8xKp2...",
      "content": "Customer prefers email over phone calls and is on a budget",
      "score": 0.94,
      "namespace": "customer:alice",
      "tags": ["preference", "contact-method"],
      "created_at": "2026-03-10T09:00:00Z"
    }
  ],
  "total": 1
}

Store a memory

POST/v1/memoriesEmbed and store a memory. Namespace is the sole organizing parameter.

Request body

FieldTypeRequiredDescription
contentstringYesThe text to embed and store. Max 32,768 characters.
namespacestringYesGrouping key. Any string works: 'customer:alice', 'project:q1', 'session:xyz'. The sole way to partition memories across users, tenants, or workflows.
tagsstring[]NoUp to 20 tags for filtering. E.g. ['preference', 'support'].
ttl_secondsintegerNoSeconds until this memory auto-deletes. Max 31,536,000 (1 year). Omit for permanent storage.
dedup_thresholdnumberNoCosine similarity (0–1). If an existing memory in the same namespace matches this threshold, it is updated instead of duplicated. 0 = always insert.
metadataobjectNoArbitrary JSON metadata attached to the memory.

Example request

{
  "content": "Customer opened a refund ticket for order #8821. Issue: wrong size delivered.",
  "namespace": "customer:acme",
  "tags": ["support", "refund", "order-issue"],
  "ttl_seconds": 2592000,
  "dedup_threshold": 0.92
}

Response - 201 Created (new) / 200 OK (deduplicated)

{
  "id": "mem_8xKp2mQ4nR...",
  "workspace_id": "ws_01JK...",
  "namespace": "customer:acme",
  "ttl_at": "2026-04-10T09:00:00Z",
  "created_at": "2026-03-10T09:00:00Z",
  "deduplicated": false
}

List memories

GET/v1/memoriesPaginated list of all memories in your workspace with optional filters

Query parameters

ParameterTypeDescription
namespacestringFilter by namespace, e.g. 'customer:acme'.
tagsstringComma-separated. Returns memories containing ALL specified tags.
limitnumberPage size. Default: 20. Max: 100.
offsetnumberNumber of records to skip. Default: 0.

Response - 200 OK

{
  "memories": [
    {
      "id": "mem_8xKp2mQ4nR...",
      "content": "Customer is price-sensitive, prefers email",
      "namespace": "customer:acme",
      "tags": ["preference"],
      "metadata": {},
      "created_at": "2026-03-10T09:00:00Z"
    }
  ],
  "total": 47,
  "limit": 20,
  "offset": 0
}

Build context window

POST/v1/memories/contextRetrieve relevant memories pre-formatted for injection into an AI system prompt

The context endpoint does the semantic search and formats the result as ready-to-inject text. Paste the returned context string directly into your LLM's system prompt.

Request body

FieldTypeRequiredDescription
querystringYesThe current user message or topic to retrieve context for.
namespacestringNoFilter to a namespace, e.g. 'customer:acme'.
tagsstring[]NoOnly include memories with these tags.
limitintegerNoMax memories to include. Default: 5. Max: 20.
thresholdnumberNoMin similarity score (0–1). Default: 0.35.
formatstringNoOutput format: system_prompt (default), bullet_list, or numbered_list.

Example request

{
  "query": "How should I greet this customer?",
  "namespace": "customer:acme",
  "limit": 5,
  "format": "system_prompt"
}

Response - 200 OK

{
  "context": "User context (3 memories):\n- Customer is price-sensitive, prefers email over phone calls (4 days ago)\n- Opened refund ticket for order #8821 (2 days ago)\n- Resolved: issued full refund (1 day ago)",
  "memories_used": 3,
  "token_estimate": 48
}

Tip: Prepend the context string to your LLM's system prompt. Use token_estimate to stay within model context limits. The system_prompt format produces the most natural results.

Export memories

GET/v1/memories/exportDownload all workspace memories as a JSONL file — for backups, audits, or migrations

Returns a application/x-ndjson stream with one JSON object per line. Accepts the same filters as GET /v1/memories. Embedding vectors are never included in the export.

Example request

# Download full workspace export
curl https://api.retainr.dev/v1/memories/export \
  -H "Authorization: Bearer rec_live_..." \
  -o memories.jsonl

# Export only a specific namespace
curl "https://api.retainr.dev/v1/memories/export?namespace=customer:acme" \
  -H "Authorization: Bearer rec_live_..." \
  -o acme_memories.jsonl

Response format (JSONL)

{"id":"mem_8xKp2...","content":"Customer prefers email","namespace":"customer:acme","tags":["preference"],"created_at":"2026-03-10T09:00:00Z"}
{"id":"mem_3fLm9...","content":"Opened refund ticket for order #8821","namespace":"customer:acme","tags":["support"],"created_at":"2026-03-08T14:22:00Z"}

Delete memories

DELETE/v1/memoriesBulk-delete memories by namespace or tags - scoped to your workspace

All deletes are scoped to your workspace. You cannot delete memories belonging to another workspace. Deletion is permanent and immediate - no soft deletes.

Request body

FieldTypeDescription
namespacestringDelete all memories in a namespace, e.g. 'customer:acme'. At least one filter is required.
tagsstring[]Delete memories matching ALL specified tags.

Example - delete all memories for a namespace (GDPR erasure)

{
  "namespace": "customer:acme"
}

Response - 200 OK

{
  "deleted": 14
}

Webhooks

Webhooks push real-time events to your URL when memories are stored or match a semantic watch query. Every delivery is signed — verify the X-Retainr-Signature header to confirm authenticity.

memory.created

Fires on every new memory stored in your workspace — regardless of content. Use for audit trails, CRM sync, or triggering downstream actions on every store.

memory.watch

Fires only when a new memory's semantic similarity to your watch_query meets the threshold. Use for keyword monitoring, churn detection, or intent alerts.

POST/v1/webhooksRegister a webhook endpoint for your workspace.

Request body

{
  "name": "string (required) — human label, e.g. 'CRM sync'",
  "url": "string (required) — must be https://",
  "event_type": "string (required) — 'memory.created' or 'memory.watch'",
  "namespace_filter": "string (optional) — only fire for memories in this namespace, e.g. 'customer:acme'",
  "watch_query": "string (required for memory.watch) — phrase to match against",
  "watch_threshold": "number (optional, default 0.75) — min cosine similarity 0.5–1.0",
  "secret": "string (optional) — HMAC secret for signature; auto-generated if omitted"
}

Example — watch for churn signals

POST /v1/webhooks
Authorization: Bearer rec_live_...

{
  "name": "Churn alert",
  "url": "https://hook.make.com/abc123",
  "event_type": "memory.watch",
  "watch_query": "cancel subscription unsubscribe want to leave",
  "watch_threshold": 0.8,
  "namespace_filter": "customer:acme"
}

Response — 201 Created

{
  "id": "wh_01JKABCDEF",
  "workspace_id": "ws_01JK...",
  "name": "Churn alert",
  "url": "https://hook.make.com/abc123",
  "event_type": "memory.watch",
  "watch_query": "cancel subscription unsubscribe want to leave",
  "watch_threshold": 0.8,
  "enabled": true,
  "secret": "a3f8...",
  "created_at": "2026-03-14T12:00:00Z"
}
GET/v1/webhooksList all webhooks registered in your workspace.

Response — 200 OK

[
  {
    "id": "wh_01JKABCDEF",
    "name": "Churn alert",
    "url": "https://hook.make.com/abc123",
    "event_type": "memory.watch",
    "watch_query": "cancel subscription",
    "watch_threshold": 0.8,
    "enabled": true,
    "consecutive_failures": 0,
    "created_at": "2026-03-14T12:00:00Z",
    "updated_at": "2026-03-14T12:00:00Z"
  }
]
PUT/v1/webhooks/{id}Update a webhook — change URL, watch query, threshold, or toggle enabled/disabled. All fields are optional; only provided fields are updated.

Request body (all optional)

{
  "name": "string",
  "url": "https://...",
  "watch_query": "string",
  "watch_threshold": 0.75,
  "enabled": true
}

Returns 204 No Content on success. Re-enabling a webhook (enabled: true) resets consecutive_failures to 0.

DELETE/v1/webhooks/{id}Permanently delete a webhook. Deliveries in-flight are not cancelled.

Returns 204 No Content on success.

POST/v1/webhooks/{id}/testSend a test delivery to the webhook URL with a synthetic memory.created payload. Useful for verifying your endpoint before going live.

Response — 200 OK

{ "delivered": true, "http_status": 200 }
// or on connection failure:
{ "delivered": false, "error": "dial tcp: connection refused" }

Payload format & signature verification

memory.created payload

{
  "event": "memory.created",
  "webhook_id": "wh_01JKABCDEF",
  "workspace_id": "ws_01JK...",
  "fired_at": "2026-03-14T15:04:05Z",
  "memory": {
    "id": "mem_01JK...",
    "content": "User prefers email over SMS",
    "namespace": "customer:acme",
    "created_at": "2026-03-14T15:04:05Z",
    "metadata": {}
  }
}

memory.watch payload (adds match object)

{
  "event": "memory.watch",
  "webhook_id": "wh_01JKABCDEF",
  "workspace_id": "ws_01JK...",
  "fired_at": "2026-03-14T15:04:05Z",
  "memory": { ... },
  "match": {
    "score": 0.91,
    "watch_query": "cancel subscription unsubscribe want to leave"
  }
}

Verifying the signature

Every delivery includes an X-Retainr-Signature header. Compute sha256=HMAC-SHA256(secret, raw_body) and compare with constant-time equality. Same pattern as Stripe and GitHub webhooks.

// Node.js verification example
const crypto = require('crypto')

function verifySignature(secret, rawBody, sigHeader) {
  const expected = 'sha256=' + crypto
    .createHmac('sha256', secret)
    .update(rawBody)
    .digest('hex')
  return crypto.timingSafeEqual(
    Buffer.from(expected),
    Buffer.from(sigHeader)
  )
}

// Express / Make.com custom webhook handler
app.post('/hook', express.raw({ type: 'application/json' }), (req, res) => {
  if (!verifySignature(process.env.RETAINR_SECRET, req.body, req.headers['x-retainr-signature'])) {
    return res.status(401).send('Invalid signature')
  }
  const event = JSON.parse(req.body)
  console.log(event.event, event.memory.content)
  res.sendStatus(200)
})

Auto-disable: Webhooks are automatically disabled after 10 consecutive delivery failures. Re-enable via PUT /v1/webhooks/{id} with { "enabled": true }. Failed deliveries can be retried individually from the dashboard.

Rate limits

Rate limits are enforced per API key, per minute. Memory operations count against your monthly plan quota separately from per-minute limits.

PlanRequests/minMemory ops/monthMax content size
Free301,0004 KB
Builder12020,0008 KB
Pro300100,0008 KB
Agency600500,00016 KB

When rate limited, the API returns 429 Too Many Requests with a Retry-After header indicating seconds to wait. n8n and Make.com modules handle retry automatically.

Error codes

All errors return a JSON body with error and message fields.

StatusError codeMeaning & resolution
400invalid_requestMalformed JSON or missing required fields. Check the request body against the schema above.
401unauthorizedMissing or invalid API key. Check your Authorization header.
403forbiddenYour API key doesn't have access to this workspace.
404not_foundThe requested memory does not exist.
422content_too_largeContent exceeds the per-plan character limit. Summarize or chunk the content.
429rate_limitedExceeded per-minute or monthly quota. Check Retry-After header and upgrade if needed.
500internal_errorUnexpected server error. We log all 500s automatically. If it persists, open a support ticket.
503embedding_unavailableEmbedding provider temporarily unavailable. Retry with exponential backoff.

Platform quick starts

n8n

Install the official retainr community node. In n8n, go to Settings - Community Nodes - Install and enter n8n-nodes-retainr. Available operations: Store Memory, Search Memory, Delete Memory.

// n8n - Store Memory node (JSON body)
{
  "content": "{{ $json.message }}",
  "namespace": "user:{{ $json.userId }}",
  "tags": ["{{ $json.intent }}"]
}

// n8n - Search Memory node (before your AI Call node)
{
  "query": "{{ $json.userMessage }}",
  "namespace": "user:{{ $json.userId }}",
  "limit": 5
}
// Output: $json.memories - inject into your system prompt

Make.com

The native Make.com app is submitted for marketplace review. Install it from the Make.com app directory: search for retainr. Available modules: Store Memory, Search Memory, List Memories, Delete Memories.

// Make.com — Store Memory module
Content: {{1.message}}
Namespace: customer:{{1.userId}}

// Make.com — Search Memory module (before your AI module)
Query: {{1.userMessage}}
Namespace: customer:{{1.userId}}
Max Results: 5
// Output: bundle of memory items — map content into your AI system prompt

Until the marketplace listing is live, use Make.com's HTTP module to call the REST API directly — set the URL to https://api.retainr.dev/v1/memories with Authorization: Bearer rec_live_....

Zapier

Coming soon

The official Zapier app is in development. In the meantime, use Webhooks by Zapier (available on all paid Zapier plans) to call retainr's REST API — POST to store, POST to search.

Want to be notified when it launches? Create a free account and we'll email you.

Ready to add memory to your workflows?

Free plan includes 1,000 memory ops per month. No credit card required.

Create free account