Primitive Memories API
Durable org-scoped or function-scoped JSON key-value storage for agents and functions. Keys are caller-defined. Function scope is always addressed by the function id UUID, not by function name.
Durable org-scoped or function-scoped JSON key-value storage for agents and functions. Keys are caller-defined. Function scope is always addressed by the function id UUID, not by function name.
Every API here is available over the APIs.io API and to AI agents over MCP.
One button, every client — Claude, Cursor, VS Code and the rest.
https://apis.io/mcp
find_apisBrowse and filter every API in the catalog.get_api_artifactsOne API's artifacts, grouped by type.get_openapiThe primary OpenAPI for this API.find_similar_apisAPIs that look like this one.apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.resolveTurn a domain, URL or GitHub org into the provider it belongs to.find_cohortsEvery scored population of providers in the catalog.curl "https://apis.io/api/v1/apis/primitive-memories-api"
curl "https://apis.io/api/v1/apis?limit=25"
Discovery needs no key. Ratings and market analysis are Pro.
Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.
A second provider on the same verified email joins the account you already have.
openapi: 3.2.0
info:
title: Primitive Account Memories API
version: 1.0.0
description: "Primitive is email infrastructure for AI agents. The Primitive API lets you manage domains, emails, webhook endpoints,\nfilters, and account settings programmatically.\n\n## Authentication\n\nMost endpoints require a Bearer token in the `Authorization` header:\n\n```\nAuthorization: Bearer prim_<your_api_key>\nAuthorization: Bearer prim_oat_<oauth_access_token>\n```\n\nAPI keys and OAuth access tokens are org-scoped. Create and manage them in your dashboard\nunder Settings > API Keys. CLI login plus CLI/agent signup endpoints\nexplicitly declare `security: []`; they do not require an API key because\nthey are used to create OAuth CLI sessions.\n\n## Rate Limiting\n\nThe API enforces a sliding window rate limit of **120 requests per\n60 seconds** per organization. When exceeded, the API returns `429`\nwith a `Retry-After` header indicating how many seconds to wait.\n\n## Pagination\n\nList endpoints use cursor-based pagination. Responses include a\n`meta` object with `total`, `limit`, and `cursor` fields. Pass the\n`cursor` value as a query parameter to fetch the next page. When\n`cursor` is `null`, there are no more results.\n\n## Response Format\n\nAll responses use a consistent envelope:\n\n```json\n{\n \"success\": true,\n \"data\": { ... },\n \"meta\": { \"total\": 42, \"limit\": 50, \"cursor\": \"...\" }\n}\n```\n\nErrors follow the same pattern:\n\n```json\n{\n \"success\": false,\n \"error\": { \"code\": \"not_found\", \"message\": \"Email not found\" }\n}\n```\n\n## Webhook signing\n\nOutbound webhook deliveries (configured via the `endpoints` API)\nare signed so receivers can verify they came from Primitive and\nhave not been tampered with in transit. The signing scheme is\ndeliberately simple so it can be reimplemented in any language\nin a few lines. The Node SDK's `verifyWebhookSignature` helper\nis the reference implementation; the wire details below let you\nwrite a verifier in Python, Go, Ruby, etc. without reading our\nsource.\n\n**Header**: `Primitive-Signature: t=<unix-seconds>,v1=<hex>`\n\nA legacy `MyMX-Signature` header is also sent on every delivery\nwith the same value, retained for back-compatibility with\nintegrations written before the rename. New code should read\n`Primitive-Signature`.\n\n**Signed string**: `${timestamp}.${rawBody}` where `timestamp`\nis the Unix-seconds integer from the `t=` parameter and\n`rawBody` is the exact bytes of the HTTP request body BEFORE\nany JSON decoding. Verify against the raw body, not a\nre-serialized parse, or you will silently mismatch on\ninsignificant whitespace.\n\n**Signature**: HMAC-SHA256 of the signed string, hex-encoded\n(lowercase). Use the account's webhook secret as the HMAC key,\nas a UTF-8 byte sequence.\n\n**Secret**: returned by `GET /account/webhook-secret`. The\nstring looks base64-shaped (e.g. `XNHBBW8VqoBjRfNs1tkZj11jTk...`)\nbut is NOT base64; use it AS-IS as a UTF-8 string for the HMAC\nkey. Base64-decoding before HMAC will silently produce\nmismatched signatures.\n\n**Tolerance**: by convention, reject deliveries whose `t=`\ntimestamp is more than 5 minutes off your wall-clock to defend\nagainst replay attacks. The Node SDK's helper enforces this by\ndefault.\n\n**Verification recipe** (any language):\n\n```\n1. Read the raw HTTP body (do not parse).\n2. Read `Primitive-Signature: t=<ts>,v1=<sig>`.\n3. Reject if abs(now - ts) > 300 seconds.\n4. expected = HMAC_SHA256_hex(secret_utf8, f\"{ts}.{rawBody}\")\n5. Constant-time compare expected to sig. Reject if not equal.\n```\n\nFor Node, use `verifyWebhookSignature` from\n`@primitivedotdev/sdk/webhook` (or the higher-level\n`handleWebhook` helper if you want a one-liner). For other\nlanguages, the recipe above is everything you need.\n\nTest deliveries: `POST /endpoints/{id}/test` triggers a fake\ndelivery to your endpoint URL, signed with your real account\nsecret, so you can confirm verification end-to-end without\nneeding real inbound mail. The test response carries the exact\n`signature` header value sent on the wire so you can compare\nstrings directly.\n\n\n## Errors\n\nEvery error response is the same JSON envelope (`{ \"success\": false, \"error\": { \"code\", \"message\" } }`), served as `application/json` with HTTP status codes, following the RFC 7807 problem-details shape. The `error.code` is a stable machine-readable string and `error.message` is human-readable.\n\n## Authorization and roles\n\nAccess is governed by organization role-based access control. Every organization member holds one of three roles — `owner`, `admin`, or `member` — and a credential inherits a role. **API keys** always act at `member` level, regardless of the role of the user who created them, so an API key can never perform owner- or admin-only actions. **OAuth access tokens** act with the authorizing user's current organization role, resolved on each request. Every operation in this spec is part of the member-level surface, so any valid credential can call it. Organization administration that is not part of this API — billing and organization settings — requires an `owner` or `admin` and is performed in the dashboard. Fine-grained per-key scopes (e.g. a send-only or read-only key) are on the roadmap; today the role model is the unit of access control.\n\n## Versioning\n\nThe current stable API is **v1**. All endpoints are served under `/v1/` and are covered by a backward-compatibility guarantee: existing fields and status codes will not change without a deprecation notice.\n\nBreaking changes are announced at least 6 months in advance via changelog and email. Deprecated operations and fields are marked `x-deprecated: true` in the spec and carry a plain-English description of the replacement. The `v1` path prefix is guaranteed stable indefinitely; backward-compatible additions (new optional fields, new endpoints) may be made at any time without a version bump."
contact:
name: Primitive
url: https://primitive.dev
license:
name: Proprietary
url: https://primitive.dev/terms
x-stability-level: stable
x-deprecation-policy: 'Breaking changes are announced at least 6 months in advance. Deprecated fields carry x-deprecated: true. The current stable version is v1.'
servers:
- url: https://api.primitive.dev/v1
description: Canonical API host (PRIMITIVE_API_BASE_URL). Carries every public API operation.
tags:
- name: Memories
description: 'Durable org-scoped or function-scoped JSON key-value storage for
agents and functions. Keys are caller-defined. Function scope is
always addressed by the function id UUID, not by function name.
'
paths:
/memories:
put:
operationId: setMemory
summary: Set a memory
description: Create or update a durable JSON memory under an org or function scope. Function-authenticated requests use their own Function id and cannot override it. If no explicit scope is provided for other credentials, `x-primitive-function-id` is used next, and other requests default to org scope. Function scope uses the function id UUID, not the function name.
tags:
- Memories
security:
- BearerAuth: []
parameters:
- name: x-primitive-function-id
in: header
required: false
description: Optional function id UUID used as the default scope for non-function-authenticated requests when the body does not include `scope`. Ignored when `scope` is provided.
schema:
type: string
format: uuid
requestBody:
required: true
content:
application/json:
schema:
type: object
additionalProperties: false
properties:
key:
type: string
minLength: 1
maxLength: 512
description: Caller-defined key, at most 512 UTF-8 bytes.
value:
description: JSON value accepted by Primitive Memories. The server accepts strings, numbers, booleans, null, arrays, and objects, validates nested values, and rejects values that do not serialize as JSON.
oneOf:
- type: 'null'
- type: string
- type: number
- type: boolean
- type: array
items:
$ref: '#/components/schemas/MemoryJsonValue'
- type: object
additionalProperties:
$ref: '#/components/schemas/MemoryJsonValue'
scope:
description: Memory scope. `org` resolves to the authenticated organization. `function` requires the function id UUID in `id`; function names are not valid scope identifiers. Function-authenticated requests cannot override their own Function scope.
oneOf:
- type: object
additionalProperties: false
properties:
type:
type: string
enum:
- org
required:
- type
- type: object
additionalProperties: false
properties:
type:
type: string
enum:
- function
id:
type: string
format: uuid
description: Function id UUID.
required:
- type
- id
ttl_seconds:
type: integer
minimum: 1
maximum: 31536000
description: Set or replace the TTL in seconds. Mutually exclusive with `expires_at` and `clear_ttl`.
expires_at:
type: string
format: date-time
description: Set or replace the absolute expiration timestamp. Mutually exclusive with `ttl_seconds` and `clear_ttl`.
clear_ttl:
type: boolean
description: Clear any existing TTL. Mutually exclusive with `ttl_seconds` and `expires_at`.
if_absent:
type: boolean
description: Create only when the key is absent. Mutually exclusive with `if_version`.
if_version:
type: string
pattern: ^[0-9]+$
description: Bigint counter serialized as a base-10 string.
required:
- key
- value
responses:
'200':
description: Existing memory updated.
content:
application/json:
schema:
allOf:
- type: object
properties:
success:
type: boolean
const: true
required:
- success
- data
- type: object
properties:
data:
type: object
additionalProperties: false
description: Memory record returned by get and set operations.
properties:
id:
type: string
format: uuid
key:
type: string
minLength: 1
maxLength: 512
description: Caller-defined key, at most 512 UTF-8 bytes.
scope:
type: object
additionalProperties: false
description: Resolved memory scope returned by the API.
properties:
type:
type: string
enum:
- org
- function
id:
type: string
format: uuid
description: Org id for org scope, function id for function scope.
required:
- type
- id
value:
description: JSON value accepted by Primitive Memories. The server accepts strings, numbers, booleans, null, arrays, and objects, validates nested values, and rejects values that do not serialize as JSON.
oneOf:
- type: 'null'
- type: string
- type: number
- type: boolean
- type: array
items:
$ref: '#/components/schemas/MemoryJsonValue'
- type: object
additionalProperties:
$ref: '#/components/schemas/MemoryJsonValue'
version:
type: string
pattern: ^[0-9]+$
description: Bigint counter serialized as a base-10 string.
created_at:
type: string
format: date-time
updated_at:
type: string
format: date-time
last_read_at:
type:
- string
- 'null'
format: date-time
description: Last successful get timestamp, or null before any get.
read_count:
type: string
pattern: ^[0-9]+$
description: Bigint counter serialized as a base-10 string.
write_count:
type: string
pattern: ^[0-9]+$
description: Bigint counter serialized as a base-10 string.
expires_at:
type:
- string
- 'null'
format: date-time
description: Expiration timestamp, or null for no TTL.
created_by:
type:
- string
- 'null'
description: Actor that created the memory, when available.
updated_by:
type:
- string
- 'null'
description: Actor that last updated the memory, when available.
required:
- id
- key
- scope
- value
- version
- created_at
- updated_at
- last_read_at
- read_count
- write_count
- expires_at
- created_by
- updated_by
headers:
ratelimit-limit:
description: Maximum number of requests allowed in the current window.
schema:
type: integer
minimum: 1
example: 120
ratelimit-remaining:
description: Remaining requests in the current window.
schema:
type: integer
minimum: 0
example: 118
ratelimit-reset:
description: Unix timestamp (seconds) when the current window resets.
schema:
type: integer
example: 1700000060
ratelimit-policy:
description: Rate-limit policy in `limit;w=seconds` format, e.g. `120;w=60`.
schema:
type: string
example: 120;w=60
'201':
description: Memory created.
content:
application/json:
schema:
allOf:
- type: object
properties:
success:
type: boolean
const: true
required:
- success
- data
- type: object
properties:
data:
type: object
additionalProperties: false
description: Memory record returned by get and set operations.
properties:
id:
type: string
format: uuid
key:
type: string
minLength: 1
maxLength: 512
description: Caller-defined key, at most 512 UTF-8 bytes.
scope:
type: object
additionalProperties: false
description: Resolved memory scope returned by the API.
properties:
type:
type: string
enum:
- org
- function
id:
type: string
format: uuid
description: Org id for org scope, function id for function scope.
required:
- type
- id
value:
description: JSON value accepted by Primitive Memories. The server accepts strings, numbers, booleans, null, arrays, and objects, validates nested values, and rejects values that do not serialize as JSON.
oneOf:
- type: 'null'
- type: string
- type: number
- type: boolean
- type: array
items:
$ref: '#/components/schemas/MemoryJsonValue'
- type: object
additionalProperties:
$ref: '#/components/schemas/MemoryJsonValue'
version:
type: string
pattern: ^[0-9]+$
description: Bigint counter serialized as a base-10 string.
created_at:
type: string
format: date-time
updated_at:
type: string
format: date-time
last_read_at:
type:
- string
- 'null'
format: date-time
description: Last successful get timestamp, or null before any get.
read_count:
type: string
pattern: ^[0-9]+$
description: Bigint counter serialized as a base-10 string.
write_count:
type: string
pattern: ^[0-9]+$
description: Bigint counter serialized as a base-10 string.
expires_at:
type:
- string
- 'null'
format: date-time
description: Expiration timestamp, or null for no TTL.
created_by:
type:
- string
- 'null'
description: Actor that created the memory, when available.
updated_by:
type:
- string
- 'null'
description: Actor that last updated the memory, when available.
required:
- id
- key
- scope
- value
- version
- created_at
- updated_at
- last_read_at
- read_count
- write_count
- expires_at
- created_by
- updated_by
'400':
$ref: '#/components/responses/ValidationError'
description: Invalid request parameters
'401':
$ref: '#/components/responses/Unauthorized'
description: Invalid or missing API key
'402':
$ref: '#/components/responses/PaymentRequired'
description: Usage credits are exhausted or payment is required.
'403':
$ref: '#/components/responses/Forbidden'
description: Authenticated caller lacks permission for the operation
'404':
$ref: '#/components/responses/NotFound'
description: Resource not found
'409':
$ref: '#/components/responses/Conflict'
description: The request conflicts with the current state of the resource
'429':
$ref: '#/components/responses/RateLimited'
description: Rate limit exceeded
get:
operationId: getMemory
summary: Get a memory
description: Fetch one active memory by key and scope. Omit scope parameters to use automatic scope resolution. Function-authenticated requests use their own Function id and cannot override it. Function scope uses a function id UUID in `scope_id`.
tags:
- Memories
security:
- BearerAuth: []
parameters:
- name: key
in: query
required: true
description: Memory key. Must be at most 512 UTF-8 bytes.
schema:
type: string
minLength: 1
maxLength: 512
- name: scope_type
in: query
required: false
description: Explicit scope type. Omit to use automatic scope resolution. Pass `function` with `scope_id=<function-id>`, or `org` with no `scope_id`. Function-authenticated requests cannot override their automatic Function scope.
schema:
type: string
enum:
- org
- function
- name: scope_id
in: query
required: false
description: Function id UUID when `scope_type=function`. Not valid with `scope_type=org`. Function-authenticated requests may only use their own Function id.
schema:
type: string
format: uuid
- name: x-primitive-function-id
in: header
required: false
description: Optional function id UUID used as the default scope for non-function-authenticated requests when query scope parameters are omitted.
schema:
type: string
format: uuid
responses:
'200':
description: Memory record with value.
content:
application/json:
schema:
allOf:
- type: object
properties:
success:
type: boolean
const: true
required:
- success
- data
- type: object
properties:
data:
type: object
additionalProperties: false
description: Memory record returned by get and set operations.
properties:
id:
type: string
format: uuid
key:
type: string
minLength: 1
maxLength: 512
description: Caller-defined key, at most 512 UTF-8 bytes.
scope:
type: object
additionalProperties: false
description: Resolved memory scope returned by the API.
properties:
type:
type: string
enum:
- org
- function
id:
type: string
format: uuid
description: Org id for org scope, function id for function scope.
required:
- type
- id
value:
description: JSON value accepted by Primitive Memories. The server accepts strings, numbers, booleans, null, arrays, and objects, validates nested values, and rejects values that do not serialize as JSON.
oneOf:
- type: 'null'
- type: string
- type: number
- type: boolean
- type: array
items:
$ref: '#/components/schemas/MemoryJsonValue'
- type: object
additionalProperties:
$ref: '#/components/schemas/MemoryJsonValue'
version:
type: string
pattern: ^[0-9]+$
description: Bigint counter serialized as a base-10 string.
created_at:
type: string
format: date-time
updated_at:
type: string
format: date-time
last_read_at:
type:
- string
- 'null'
format: date-time
description: Last successful get timestamp, or null before any get.
read_count:
type: string
pattern: ^[0-9]+$
description: Bigint counter serialized as a base-10 string.
write_count:
type: string
pattern: ^[0-9]+$
description: Bigint counter serialized as a base-10 string.
expires_at:
type:
- string
- 'null'
format: date-time
description: Expiration timestamp, or null for no TTL.
created_by:
type:
- string
- 'null'
description: Actor that created the memory, when available.
updated_by:
type:
- string
- 'null'
description: Actor that last updated the memory, when available.
required:
- id
- key
- scope
- value
- version
- created_at
- updated_at
- last_read_at
- read_count
- write_count
- expires_at
- created_by
- updated_by
headers:
ratelimit-limit:
description: Maximum number of requests allowed in the current window.
schema:
type: integer
minimum: 1
example: 120
ratelimit-remaining:
description: Remaining requests in the current window.
schema:
type: integer
minimum: 0
example: 118
ratelimit-reset:
description: Unix timestamp (seconds) when the current window resets.
schema:
type: integer
example: 1700000060
ratelimit-policy:
description: Rate-limit policy in `limit;w=seconds` format, e.g. `120;w=60`.
schema:
type: string
example: 120;w=60
'400':
$ref: '#/components/responses/ValidationError'
description: Invalid request parameters
'401':
$ref: '#/components/responses/Unauthorized'
description: Invalid or missing API key
'402':
$ref: '#/components/responses/PaymentRequired'
description: Usage credits are exhausted or payment is required.
'403':
$ref: '#/components/responses/Forbidden'
description: Authenticated caller lacks permission for the operation
'404':
$ref: '#/components/responses/NotFound'
description: Resource not found
'429':
$ref: '#/components/responses/RateLimited'
description: Rate limit exceeded
delete:
operationId: deleteMemory
summary: Delete a memory
description: Delete one active memory by key and scope. Function-authenticated requests use their own Function id and cannot override it. Deletes are idempotent without `if_version`; a stale `if_version` returns `memory_conflict`.
tags:
- Memories
security:
- BearerAuth: []
parameters:
- name: key
in: query
required: true
description: Memory key. Must be at most 512 UTF-8 bytes.
schema:
type: string
minLength: 1
maxLength: 512
- name: scope_type
in: query
required: false
description: Explicit scope type. Omit to use automatic scope resolution. Pass `function` with `scope_id=<function-id>`, or `org` with no `scope_id`. Function-authenticated requests cannot override their automatic Function scope.
schema:
type: string
enum:
- org
- function
- name: scope_id
in: query
required: false
description: Function id UUID when `scope_type=function`. Not valid with `scope_type=org`. Function-authenticated requests may only use their own Function id.
schema:
type: string
format: uuid
- name: if_version
in: query
required: false
description: Optional compare-and-delete version.
schema:
type: string
pattern: ^[0-9]+$
description: Bigint counter serialized as a base-10 string.
- name: x-primitive-function-id
in: header
required: false
description: Optional function id UUID used as the default scope for non-function-authenticated requests when query scope parameters are omitted.
schema:
type: string
format: uuid
responses:
'200':
description: Delete result.
content:
application/json:
schema:
allOf:
- type: object
properties:
success:
type: boolean
const: true
required:
- success
- data
- type: object
properties:
data:
type: object
# --- truncated at 32 KB (53 KB total) ---
# Full source: https://raw.githubusercontent.com/api-evangelist/primitive/refs/heads/main/openapi/primitive-memories-api-openapi.yml