Skip to content

REST API

Matt Dula edited this page Apr 18, 2026 · 1 revision

REST API

Nakatomi's REST surface is fully documented by its OpenAPI spec — it's the canonical reference because it stays in sync with the code automatically.

Interactive docs

When Nakatomi is running:

  • Swagger UI: GET /docs
  • ReDoc: GET /redoc
  • Raw OpenAPI: GET /openapi.json

Import /openapi.json into:

  • Postman / Insomnia — full collection, one click
  • ChatGPT Custom GPT Actions — becomes the tool list
  • Your IDE's OpenAPI plugin
  • Code generators (openapi-generator, etc.)

Endpoint families

Prefix Purpose Wiki link
/auth signup, login, /me Authentication
/workspace workspace settings, members, API keys Authentication
/contacts CRUD + bulk upsert + search
/companies CRUD + bulk upsert + search
/pipelines pipelines + stages
/deals CRUD, status transitions
/activities call / meeting / email_log entries
/notes markdown notes on any entity
/tasks assignable tasks with due dates
/relationships typed graph edges + neighbors BFS
/timeline append-only event stream
/webhooks subscriptions + delivery log Webhooks
/files upload / download / list
/memory recall / link / trace / inbound webhook Memory-Connectors
/ingest normalize external data into CRM rows Ingest
/custom-fields workspace-scoped field registry Custom-Fields
/export + /import portable JSON dumps Export-Import
/schema, /llms.txt, /.well-known/agent.json discovery
/health liveness
/dashboard optional local UI Dashboard

Conventions

  • Auth: Authorization: Bearer nk_<key> (API keys) or Bearer <jwt> + X-Workspace: <slug> (user JWTs). See Authentication.
  • Pagination: every list endpoint returns {"items": [...], "next_cursor": "...", "count": N}. Pass ?limit= (1-500) and ?cursor=<next_cursor> to page.
  • Idempotency: any mutating request can carry an Idempotency-Key header. Replays return the original response if the body matches.
  • Soft delete: DELETE /contacts/{id} soft-deletes by default; ?hard=true to bypass.
  • Bulk upsert: POST /contacts/bulk_upsert + /companies/bulk_upsert. Matches on external_id first, then email/domain.
  • Filtering: each list endpoint accepts resource-specific query params (e.g. ?email=, ?tag=, ?status=). See Swagger for the per-endpoint catalog.

Rate limits

If an API key carries a rate_limit_per_minute, over-limit requests get 429 Too Many Requests with a Retry-After header. See Rate-Limiting.

Errors

Shape:

{"error": "human-readable string"}

Status codes:

  • 400 — validation error (bad input shape)
  • 401 — missing or invalid token
  • 403 — authenticated but not allowed (role check)
  • 404 — resource not found
  • 409 — conflict (e.g. unique-constraint hit, idempotency-key reused with a different body)
  • 422 — semantic validation (bad enum value, unsupported schema_version)
  • 429 — rate limit exceeded
  • 5xx — bug; see logs

Quick examples

KEY=nk_abc_123
BASE=http://localhost:8000

# List contacts
curl -s $BASE/contacts -H "Authorization: Bearer $KEY"

# Create a contact
curl -s -X POST $BASE/contacts \
  -H "Authorization: Bearer $KEY" \
  -H "Content-Type: application/json" \
  -d '{"first_name":"Ada","email":"ada@example.com","external_id":"ada-1"}'

# Bulk upsert
curl -s -X POST $BASE/contacts/bulk_upsert \
  -H "Authorization: Bearer $KEY" \
  -H "Content-Type: application/json" \
  -d '[{"external_id":"a1","email":"a@ex.com"},{"external_id":"a2","email":"b@ex.com"}]'

# Move a deal
curl -s -X PATCH $BASE/deals/<id> \
  -H "Authorization: Bearer $KEY" \
  -H "Content-Type: application/json" \
  -d '{"stage_id":"<won-stage-id>","status":"won"}'

# Fetch timeline for an entity
curl -s "$BASE/timeline/deal/<id>?limit=100" -H "Authorization: Bearer $KEY"

For richer examples see MCP-Tools (the MCP tool set has the same shapes).

Clone this wiki locally