Extraction API
The Extraction API converts an agent's tool-call trace into a replayable procedure. Parsing is deterministic — no generative model is involved, so the same input always produces byte-identical output. The API is stateless: request bodies are never stored or logged. The only server-side state is a hash of each issued key and anonymous per-request usage metrics. The resulting procedure is written into your own database, on your side, through your existing GBrain connection.
You are integrating a harness or agent with the Memorable Extraction API.
Base URL: https://memorable-extraction-api.memorable.workers.dev
1. Get a key once (no account): POST /v1/keys with {} -> { "api_key": "mk_..." }.
Store it; send it as "Authorization: Bearer mk_...". Issuance: 10/min and 50/hour per IP.
2. After each finished session, POST /v1/extract with:
{ "session_id": "<stable id>", "harness": "<your harness name, any string>",
"task_description": "<one line: what the task was>", "skip_embedding": true,
"tool_calls": [ { "name": "<tool>", "input": { "command"|"file_path"|"path"|
"pattern"|"url"|"query": "<string>" }, "result": { "ok": true|false } |
{ "exit_code": 0 } } ] }
Send ONLY these fields. Do not send conversation text, file contents, or
credentials; include "result" only when the outcome is actually known.
3. Response: { "draft": { title, steps[{seq, action, activity_class, command?,
repeat_count, targets?, creates?}], trigger_signature{entities, search_text}, preconditions,
postconditions, ... }, "request_id": "..." }. Parsing is deterministic:
identical input yields an identical draft. Store the draft yourself, on the
user's side; the API keeps nothing.
4. Errors: 401 unauthorized, 400 invalid_json/invalid_request, 413 over 8MB,
429 rate_limited (300/min per key). Every response carries request_id;
include it when reporting problems.Base URL
https://memorable-extraction-api.memorable.workers.devAuthentication
Every request requires a bearer token in the Authorization header. Requests without a valid token return 401. Keys look like mk_… and are issued self-serve — there is no sign-up. Only a SHA-256 hash of your key is ever stored server-side.
Authorization: Bearer $MEMORABLE_API_KEY
Every response carries a request_id (also in the x-memorable-request-id header). Include it when reporting a problem — it lets us find your exact request.
Get a key
/v1/keysIssues a fresh API key. No authentication, no account, no email — the only unauthenticated endpoints are this and GET /healthz, and issuance is tightly rate-limited per IP. If you use the CLI you never call this yourself: memorable init issues a key automatically and saves it to ~/.memorable/config.json.
curl -X POST https://memorable-extraction-api.memorable.workers.dev/v1/keys \
-H "Content-Type: application/json" -d '{}'
# → { "api_key": "mk_…", "request_id": "…" }Create a procedure
/v1/extractConverts a trace into a ProcedureDraft. Any harness is accepted: known harnesses (claude-code, codex, opencode) get curated activity registries; every other harness string is served by a generic tier that still infers execution from command-shaped input.
Body parameters
session_idstringrequiredtool_callsarray of ToolCallRecordrequirednamestringrequiredinputobjectrequiredresultobjectoptional{ ok?: boolean, exit_code?: number }. Powers postcondition detection — success is derived from real outcomes, never guessed from a command's name. Omitting it is valid; postconditions simply come back empty.task_descriptionstringoptionalcorpusstringoptionalharnessstringoptionalskip_embeddingbooleanoptionalResponse fields
draft.titlestringrequiredtask_description or the first meaningful transcript line.draft.stepsarrayrequired{ seq, action, activity_class, command?, repeat_count, targets?, creates? }. Retry cycles collapse into one step with repeat_count recording the repetitions.draft.steps[].activity_classstringrequiredread, write, search, execute, other. A shell line is classified from the command itself, deterministically: pure reads and searches are downgraded out of execute, file mutations are upgraded to write, and anything ambiguous stays execute. Without this a harness that funnels everything through one shell tool records no reads at all, and with no reads there are no dependencies between procedures.draft.steps[].targetsarray of stringoptionalcommand; present but empty means the line was parsed and touched none.draft.steps[].createsbooleanoptionaldraft.trigger_signatureobjectrequireddraft.preconditionsarray of stringrequireddraft.postconditionsarray of stringrequireddraft.embeddingarray of numberrequired[] when skipped or the provider failed. Failure never blocks the draft.draft.embedding_modelstringrequiredcurl https://memorable-extraction-api.memorable.workers.dev/v1/extract \
-H "Authorization: Bearer $MEMORABLE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"session_id": "run-183",
"task_description": "rotate the TLS cert for api.example.com",
"harness": "my-python-orchestrator",
"tool_calls": [
{"name": "shell",
"input": {"command": "certbot renew"},
"result": {"ok": true}},
{"name": "shell",
"input": {"command": "nginx -s reload"},
"result": {"ok": true}}
]
}'{
"draft": {
"title": "rotate the TLS cert for api.example.com",
"session_id": "run-183",
"schema_version": "1.0.0",
"trigger_signature": {
"summary_text": "rotate the TLS cert for api.example.com",
"entities": {
"file_paths": [],
"commands": ["certbot renew", "nginx -s reload"],
"tool_names": ["shell"]
},
"search_text": "rotate the TLS cert ..."
},
"steps": [
{ "seq": 1, "action": "shell", "activity_class": "execute",
"command": "certbot renew", "repeat_count": 1 },
{ "seq": 2, "action": "shell", "activity_class": "execute",
"command": "nginx -s reload", "repeat_count": 1 }
],
"preconditions": [],
"postconditions": [
"final command exited successfully: nginx -s reload"
],
"embedding": [],
"embedding_model": ""
}
}Embed a query
/v1/embedQuery-side embedding for recall. Used only as a fallback — recall tries exact and lexical matching locally first (zero tokens, zero network), and the CLI prefers the embedding provider your GBrain already has configured.
textstringrequiredcurl https://memorable-extraction-api.memorable.workers.dev/v1/embed \
-H "Authorization: Bearer $MEMORABLE_API_KEY" \
-H "Content-Type: application/json" \
-d '{"text": "rotate the TLS cert"}'Errors
Errors return JSON with an error code and, where useful, a detail message.
| Status | Code | Meaning |
|---|---|---|
| 401 | unauthorized | Missing or invalid bearer token. |
| 400 | invalid_request | Missing session_id or tool_calls; detail says what's expected. |
| 400 | invalid_json | The request body is not valid JSON. |
| 413 | payload_too_large | Body over 8 MB. |
| 429 | rate_limited | Over the per-key limit (or key-issuance per-IP limit); body carries retry_after_s. |
| 404 | not_found | Unknown path or method. |
| 503 | keys_unavailable | Key issuance temporarily unavailable. |
An embedding-provider failure is not an error: the draft still returns with embedding: [] and an embedding_error field, and recall degrades to lexical and exact matching.
Rate limits
300 requests per 60 seconds per API key, and 3 key issuances per 60 seconds per IP, enforced at the edge. A typical integration makes one /v1/extract call per completed long-running session, which sits far below the limit.
Security
- Stateless by design. No database, no persistence — request bodies are processed in memory and discarded. Bodies are never logged.
- Your database stays yours. The API never receives connection credentials. Writes happen on your machine through your existing GBrain connection, into an isolated source.
- Pre-scanned input only.The CLI sends only content that has already passed GBrain's secret scanner, and refuses to send a session that was written unscanned.
- Injection-hardened recall.Stored procedures are re-rendered as explicitly inert reference data, stripped of control characters and size-capped, before they ever reach an agent's context.