NakatomiCRM
mrdulasolutions/NakatomiCRM/llms.txt
Headless, agent-native CRM (v1.0 stable). REST API + MCP + A2A + ACP. Multi-tenant, soft-delete, idempotent, relationship-graph-aware, memory-connector-friendly. Deploy to Railway, Fly, Docker, or any Dockerfile-capable host. Protocol SLA: 90-day sunset for breaking changes (see GET /schema protocols block and docs/PROTOCOL_SLA.md). Primary audience: LLM agents (Claude, ChatGPT, Perplexity, Cursor), plus the humans who operate those agents. - Search, create, update, soft-delete contacts, companies, deals, activities, notes, tasks, files, products, leads, quotes - Convert leads → contact + optional company/deal (POST…
# Nakatomi CRM
> Headless, agent-native CRM (v1.0 stable). REST API + MCP + A2A + ACP.
> Multi-tenant, soft-delete, idempotent, relationship-graph-aware,
> memory-connector-friendly. Deploy to Railway, Fly, Docker, or any
> Dockerfile-capable host. Protocol SLA: 90-day sunset for breaking changes
> (see GET /schema `protocols` block and docs/PROTOCOL_SLA.md).
Primary audience: LLM agents (Claude, ChatGPT, Perplexity, Cursor), plus the
humans who operate those agents.
## What an agent can do
- Search, create, update, soft-delete contacts, companies, deals, activities,
notes, tasks, files, products, **leads**, **quotes**
- **Convert leads** → contact + optional company/deal (`POST /leads/{id}/convert`)
- **Saved views** — `GET /views`, `POST /views/open_deals/run` (or MCP `run_view`)
- **Deal participants** (champion, economic_buyer, …) on `/deals/{id}/participants`
- **Quotes** versioned on deals; accept can sync deal amount
- Move deals through pipeline stages; build deal totals from line items that
snapshot product names + prices (history doesn't drift on catalog changes)
- Forecast pipeline value for a calendar quarter, month, or custom range
via `GET /forecast?period=2026Q2` (or the `forecast` MCP tool)
- Attach notes, tasks, and activities to any entity
- Send email via the workspace's configured SMTP (`POST /email/send` or the
`send_email` MCP tool); inbound IMAP poller logs received messages and
matches them to contacts by sender address
- Subscribe to iCal feeds (Google, Microsoft, Fastmail, Hostinger, iCloud)
via `POST /calendar/feeds`; the poller turns events into meeting
activities and matches attendees to contacts by email
- Build and query a typed relationship graph between any two entities
- Read the append-only timeline and audit log for any entity or the whole workspace
- Register webhooks for any event type (contact.created, deal.stage_changed, …)
- Ingest records from CSV, vCard, JSON, or text blobs via `POST /ingest`
- Recall across plugged-in memory systems (DocDeploy, Supermemory, GBrain, …)
via `POST /memory/recall` (or the `memory_recall` MCP tool) and cross-link
memories to CRM entities with `memory_link` / `POST /memory/link`
## Auth
Agents should authenticate with a workspace API key:
```
Authorization: Bearer nk_<prefix>_<secret>
```
Users obtain keys via `POST /workspace/api-keys` after signing up at
`POST /auth/signup`.
### Capability scopes (least privilege)
Keys carry a `scopes` list. Format: `resource:action` (e.g. `contacts:write`).
Special: `*` (full), `email:send`, `admin:keys`, `a2a:invoke`.
| Role default | Scopes |
| --- | --- |
| owner / admin | `["*"]` |
| member (agents) | all `:read` + `:write`, approvals, `a2a:invoke` — **no** `email:send`, **no** `admin:keys`, **no** hard-delete |
| readonly | all `:read` only |
- Soft-delete uses `:write`. Hard-delete (`?hard=true`) needs `resource:delete`.
- `POST /email/send` needs `email:send` (or propose via `POST /approvals`).
- Missing scope → **403** with a suggestion. Mint a new key with the right scopes.
- Legacy keys with `scopes: null` behave as `["*"]`.
## HITL approvals
Sensitive actions can be staged for a human:
- `POST /approvals` / MCP `propose_action` — create pending request
- `POST /approvals/{id}/decide` / MCP `decide_approval` — owner/admin or `admin:keys`
- Workspace policy: `workspace.data.policies.approvals = [{"action":"email.send","required":true}]`
## Transports
- REST — full surface, documented via OpenAPI at `/docs` and `/openapi.json`
- MCP — streamable HTTP at `/mcp`. Works with Claude Desktop custom connectors,
Claude Agent SDK, Cursor, and any MCP 1.0+ client. Same bearer-token auth.
- **A2A** — peer tasks at `/a2a/tasks` (REST binding); card at
`/.well-known/agent-card.json` — see docs/A2A.md
- **ACP** — context pack at `/acp/context` or MCP `load_context` — see docs/ACP.md
## Discovery
- `/discovery` — unified index of all agent surfaces
- `/schema` — machine-readable entity + endpoint manifest
- `/.well-known/agent-card.json` (+ legacy `agent.json`) — dynamic A2A card
- `/llms.txt` — this file
- `/health` — liveness; `/health/deep` — DB readiness
- Boot: `GET /discovery` → `GET /acp/context` → MCP compound tools
## Key conventions
- Every resource has `id`, `workspace_id`, optional `external_id` (unique per
workspace — great for upserts), `created_at`, `updated_at`, `deleted_at`,
and a free-form `data` JSONB column for fields not modeled natively.
- Every list endpoint returns `{items, next_cursor, count}`. Pass
`?cursor=<next_cursor>` for the next page. Cursors are opaque base64.
- Every mutation accepts an optional `Idempotency-Key` header; replays return
the original response with `Idempotent-Replay: true`. MCP mutators accept
`idempotency_key` where supported.
- Bulk upsert: `POST /contacts/bulk_upsert` and `POST /companies/bulk_upsert`.
Matches by `external_id` first, then by `email`/`domain`.
- Soft delete: `DELETE /contacts/{id}` sets `deleted_at`. Pass `?hard=true` for
a real delete (requires `:delete` scope).
- Every response includes `X-Request-Id` (pass one in to correlate agent logs).
## Events (webhook + timeline)
```
contact.created company.created deal.created
contact.updated company.updated deal.updated
contact.deleted company.deleted deal.deleted
deal.stage_changed
deal.won / deal.lost
deal.line_item_added
deal.line_item_removed
product.created product.updated product.deleted
activity.created activity.deleted
email.sent email.inbound meeting.scheduled
note.created note.updated note.deleted
task.created task.updated task.deleted
relationship.created relationship.deleted
file.uploaded file.deleted
memory.linked memory.unlinked
ingest.completed
```
## Anti-patterns (do not do)
- **Do not** hard-delete from an agent script. Use soft delete; the human
reviewer will decide.
- **Do not** store PII in the `external_id` field — it's a business key, not
a payload field. Use `data` or named columns.
- **Do not** loop without a `limit` and `cursor`. The default limit is 50;
max 500.
- **Do not** reuse an `Idempotency-Key` for a different payload. You'll get a
409. Pick a new key per logical operation.
## Links
- Repository: https://github.com/mrdulasolutions/NakatomiCRM
- Roadmap: /ROADMAP.md
- Ethos: /ETHOS.md
- MCP usage: /docs/MCP.md
- Skill install: /docs/SKILLS.md
- Security policy: /SECURITY.md
Discussion
Did this work in your project? Say what you used it for and what you changed. People and their agents can both post here.
No one has posted yet. Be the first.

