Rekor — Data Layer for AI Agents
You have access to the rekor CLI — the builder interface for Rekor. Use it to set up schemas, configure collections, test in preview environments, and promote to production. Production agents use MCP tools to read/write records; external systems integrate via REST API. The CLI is your tool for designing and managing the data model.
Setup
All commands require --workspace (or set REKOR_WORKSPACE env var). Start by listing or creating a workspace:
rekor workspaces list
rekor workspaces create my-workspace --name "My Workspace"
Core Concepts
- Collection: A schema (JSON Schema) that defines a record type. No migrations — create at runtime.
- Record: A JSON document conforming to a collection's schema.
- Relationship: A typed, directed link between two records with optional metadata.
Quick Start
1. Create a collection
rekor collections upsert invoices --workspace my-ws \
--name "Invoices" \
--schema '{"type":"object","properties":{"customer":{"type":"string"},"amount":{"type":"number"},"status":{"type":"string","enum":["draft","issued","paid"]}},"required":["customer","amount"]}'
2. Create a record
rekor records upsert invoices --workspace my-ws \
--data '{"customer":"Acme Corp","amount":5000,"status":"draft"}'
With external ID for idempotent upsert:
rekor records upsert invoices --workspace my-ws \
--data '{"customer":"Acme Corp","amount":5000}' \
--external-id inv_123 --external-source billing
3. Query records
rekor sql "SELECT data.invoice_number.:String as num, data.status.:String as status, arraySum(CAST(data.line_items[].amount, 'Array(Float64)')) as total FROM records FINAL WHERE workspace_id = {workspace_id:String} AND collection = 'invoices' AND deleted = false ORDER BY total DESC" --workspace my-ws
4. Link records
rekor relationships upsert --workspace my-ws \
--source invoices/rec_abc --target customers/rec_xyz \
--type belongs_to
5. Traverse relationships
rekor query-relationships invoices rec_abc --workspace my-ws \
--type belongs_to --direction outgoing
---
Full Command Reference
Workspaces
rekor workspaces list [--tag <tag>]
rekor workspaces get <id>
rekor workspaces create <id> --name <name> [--description <desc>] [--tags <comma-separated>]
rekor workspaces tag <id> --tags <comma-separated>
rekor workspaces delete <id>
Tags let you group workspaces (e.g., client:acme,billing). Filter with --tag.
Collections
rekor collections list --workspace <ws>
rekor collections get <id> --workspace <ws>
rekor collections upsert <id> --workspace <ws> --name <name> --schema <json|@file>
rekor collections delete <id> --workspace <ws>
Records
rekor records upsert <collection> --workspace <ws> --data <json|@file> [--id <uuid>] [--external-id <id>] [--external-source <src>]
rekor records get <collection> <id> --workspace <ws>
rekor records delete <collection> <id> --workspace <ws>
SQL Query
Execute read-only SQL queries directly against workspace data. Supports filtering, aggregation, JOINs, CTEs, and array operations.
rekor sql "<query>" --workspace <ws> [--param key=value ...] [--file query.sql]
Tables: records, relationships, collections, workspaces, operations_log
Important: Always include workspace_id = {workspace_id:String}, deleted = false, and FINAL after table names.
Accessing JSON fields: Use data.field.:Type subcolumn syntax for the native JSON type. Use CAST(data.field, 'Type') when type-safe conversion is needed (e.g., integers stored as Int64 vs Float64).
Examples:
# Simple query
rekor sql "SELECT data.invoice_number.:String as num, data.status.:String as status FROM records FINAL WHERE workspace_id = {workspace_id:String} AND collection = 'invoices' AND deleted = false" --workspace my-ws
Aggregation
rekor sql "SELECT data.status.:String as status, count() as cnt FROM records FINAL WHERE workspace_id = {workspace_id:String} AND collection = 'invoices' AND deleted = false GROUP BY status" --workspace my-ws
Array aggregation (sum embedded line items)
rekor sql "SELECT data.invoice_number.:String as num, arraySum(CAST(data.line_items[].amount, 'Array(Float64)')) as total FROM records FINAL WHERE workspace_id = {workspace_id:String} AND collection = 'invoices' AND deleted = false" --workspace my-ws
Explode array elements with ARRAY JOIN
rekor sql "SELECT item.description.:String as item, sum(CAST(item.amount, 'Float64')) as revenue FROM records FINAL ARRAY JOIN data.line_items[] as item WHERE workspace_id = {workspace_id:String} AND collection = 'invoices' AND deleted = false GROUP BY item ORDER BY revenue DESC" --workspace my-ws
CTE joining records with relationships
rekor sql "WITH inv AS (SELECT id, data.invoice_number.:String as num, arraySum(CAST(data.line_items[].amount, 'Array(Float64)')) as total FROM records FINAL WHERE workspace_id = {workspace_id:String} AND collection = 'invoices' AND deleted = false), pay AS (SELECT target_id, sum(CAST(data.allocated, 'Float64')) as paid FROM relationships FINAL WHERE workspace_id = {workspace_id:String} AND rel_type = 'payment_for' AND deleted = false GROUP BY target_id) SELECT inv.num, inv.total, coalesce(pay.paid, 0) as paid, inv.total - coalesce(pay.paid, 0) as balance FROM inv LEFT JOIN pay ON pay.target_id = inv.id ORDER BY balance DESC" --workspace my-ws
With parameters
rekor sql "SELECT FROM records FINAL WHERE workspace_id = {workspace_id:String} AND data.status.:String = {status:String} AND deleted = false" --workspace my-ws --param status=issued
Relationships
rekor relationships upsert --workspace <ws> --source <col/id> --target <col/id> --type <type> [--id <id>] [--data <json>]
rekor relationships get <id> --workspace <ws>
rekor relationships delete <id> --workspace <ws>
rekor query-relationships <collection> <id> --workspace <ws> [--type <type>] [--direction outgoing|incoming|both]
Hooks (inbound webhooks)
External systems push data into Rekor via hooks. Each hook provides a unique ingest URL.
rekor hooks create --workspace <ws> --name <name> --collection <collection>
rekor hooks list --workspace <ws>
rekor hooks get <id> --workspace <ws>
rekor hooks delete <id> --workspace <ws>
Hooks can only be created/deleted in preview workspaces. Promote to production when ready.
Triggers (outbound webhooks)
Triggers fire automatically when records change, notifying external systems via HTTP POST.
rekor triggers create --workspace <ws> --name <name> --collection <collection> --url <url> --events '["create","update","delete"]'
rekor triggers list --workspace <ws>
rekor triggers get <id> --workspace <ws>
rekor triggers delete <id> --workspace <ws>
Triggers are HMAC-signed (X-Rekor-Signature) and fire asynchronously. By default, writes from hooks don't re-fire triggers (skip_hook_writes: true). Triggers can only be created/deleted in preview workspaces.
Batch (atomic)
Execute up to 1,000 operations atomically — all succeed or all fail:
rekor batch --workspace <ws> --operations '[
{"type":"upsert_record","collection":"invoices","data":{"customer":"A","amount":100}},
{"type":"upsert_record","collection":"invoices","data":{"customer":"B","amount":200}},
{"type":"upsert_relationship","rel_type":"related_to","source_collection":"invoices","source_id":"id1","target_collection":"invoices","target_id":"id2"}
]'
Operation types: upsert_record, delete_record, upsert_relationship, delete_relationship, upsert_collection, delete_collection
Provider Adapters
Import tool definitions from any LLM provider as collections, or export collections as tool definitions.
Import (creates collections from tool definitions):# OpenAI
rekor providers import openai --workspace <ws> --tools '[{"type":"function","function":{"name":"create_invoice","parameters":{"type":"object","properties":{"customer":{"type":"string"},"amount":{"type":"number"}},"required":["customer"]}}}]'
Anthropic
rekor providers import anthropic --workspace <ws> --tools '[{"name":"create_invoice","input_schema":{"type":"object","properties":{"customer":{"type":"string"}}}}]'
MCP
rekor providers import mcp --workspace <ws> --tools '[{"name":"create_invoice","inputSchema":{"type":"object","properties":{"customer":{"type":"string"}}}}]'
From file
rekor providers import openai --workspace <ws> --tools @tools.json
Export (get collections as tool definitions):
rekor providers export openai --workspace <ws>
rekor providers export anthropic --workspace <ws> --collections invoices,customers
rekor providers export mcp --workspace <ws> --output tools.json
Import tool call (create a record from provider-native format):
# OpenAI tool call → record
rekor providers import-call openai invoices --workspace <ws> \
--data '{"arguments":{"customer":"Acme","amount":5000}}' \
--external-id call_abc123 --external-source openai
Anthropic tool call → record
rekor providers import-call anthropic invoices --workspace <ws> \
--data '{"input":{"customer":"Acme","amount":5000}}'
Supported providers: openai, anthropic, google, mcp
MCP Factory (custom MCP endpoints)
Create purpose-built MCP servers from your workspace collections. Each endpoint serves domain-specific tools — agents see create_invoice, list_payments, not generic Rekor operations.
# Create a curated MCP endpoint
rekor endpoints upsert invoicing-agent --workspace my-ws \
--name "Invoicing Agent" \
--tool "invoices:get,list" \
--tool "payments:create,get,list" \
--relationship "invoice_payment:list" \
--batch "invoices:create,update" --batch "payments:create" --batch "invoice_payment:create" \
--sql-query
Get the MCP connection URL
rekor endpoints url invoicing-agent
→ https://mcp.rekor.pro/e/invoicing-agent/mcp
List all endpoints
rekor endpoints list --workspace my-ws
Get endpoint config (with resolved schemas)
rekor endpoints get invoicing-agent --workspace my-ws --resolved
Delete an endpoint
rekor endpoints delete invoicing-agent --workspace my-ws
Tool spec format: collection:op1,op2 (operations: create, get, list, update, delete)
Relationship spec format: rel_type:op1,op2 (operations: create, list, delete)
Batch spec format: collection_or_rel:op1,op2
Custom tool names and descriptions: Use --config with full JSON for advanced control:
rekor endpoints upsert invoicing-agent --workspace my-ws --config '{
"name": "Invoicing Agent",
"tools": [
{
"collection": "invoices",
"operations": ["get", "list"],
"name_override": "search_invoices",
"description_override": "Search invoices by customer, status, or date range"
},
{
"collection": "payments",
"operations": ["create", "get", "list"],
"description_override": "Manage payment records for invoices"
}
],
"relationships": [
{
"rel_type": "invoice_payment",
"operations": ["create", "list"],
"name_override": "assign_payment",
"description_override": "Link a payment to an invoice"
}
],
"sql_query": true
}'
Or from a file: --config @endpoint.json
Connect agents to the endpoint URL with a token scoped to exactly one workspace. The agent sees only the tools you configured — fully domain-specific, no Rekor concepts.
Endpoints can only be created/modified in preview workspaces. Promote to production when ready.
API Tokens
Create scoped tokens for agents, integrations, and CI/CD. Tokens can be restricted to specific workspaces, collections, environments, and permissions. Tokens are hashed before storage — the raw value is shown only once on creation.
# Create a full-access token
rekor tokens create --name "my-key" --grants '[{"scope":{"workspaces":[""]},"permissions":[""]}]'
Create a scoped token (read-only on one workspace)
rekor tokens create --name "client-a-reader" \
--grants '[{"scope":{"workspaces":["client-a"],"environments":["production"]},"permissions":["read:records","read:collections"]}]'
Create a token with expiration
rekor tokens create --name "temp-key" \
--grants '[{"scope":{"workspaces":[""]},"permissions":[""]}]' \
--expires-at 2026-12-31T23:59:59Z
List tokens (shows status, last_used_at, expires_at)
rekor tokens list
Revoke a token
rekor tokens revoke <token_id>
Permissions: read:records, write:records, read:collections, write:collections, read:relationships, write:relationships, read:attachments, write:attachments, read:hooks, write:hooks, read:triggers, write:triggers, read:endpoints, write:endpoints, read:workspaces, write:workspaces, or for all.
Scope fields: workspaces (required), collections (optional — omit for all), environments (optional — production, preview, or omit for both).
Tokens enforce a privilege ceiling — you can only create tokens with equal or narrower scope than your own.
---
Output Format
Add --output json for machine-readable JSON output (default is table).
rekor records get invoices rec_abc --workspace my-ws --output json
Data Input
Any --data, --schema, --tools, or --operations flag accepts:
- Inline JSON:
'{"key":"value"}' - File reference:
@path/to/file.json
Environments
Workspaces are either production or preview. As an agent, you can:
- Read from any workspace (production or preview)
- Write records and relationships to any workspace
- Modify schemas (collections, triggers, hooks) only in preview workspaces
To modify schemas, create a preview workspace first:
rekor workspaces create-preview my-workspace --name "add-invoices"
Then work in the preview workspace:
rekor collections upsert invoices --workspace my-workspace--add-invoices \
--schema '{"type":"object","properties":{"amount":{"type":"number"}}}'
When you're done, ask a human operator to promote your changes:
# Human runs: rekor workspaces promote my-workspace --from my-workspace--add-invoices
Promotion is a human-only operation. You cannot promote directly. Always work in preview for schema changes.
Best Practices
- Use external IDs for idempotent upserts — retry-safe, no duplicates.
- One workspace per context — keeps data isolated (per test run, per agent, per environment).
- Preview for schema changes — modify collections, triggers, hooks in a preview workspace. Ask a human to promote.
- Batch for atomicity — when multiple writes must succeed or fail together.
- Relationships over nested data — link records instead of embedding. Enables traversal and flexible queries.
- Schema first — define the collection schema before writing records. Validation catches bad data early.
- Query delay after writes —
sqlandquery-relationshipsmay take ~1-2s to reflect new writes. Single-record reads (records get,relationships get) are instant. If you need to query right after writing, wait briefly or use direct gets. - Set token expiration — use
--expires-atfor short-lived tokens. Revoke unused tokens promptly. - Save tokens immediately — the raw token is shown only once on creation. Store it securely before closing the terminal.