# Richbee > Agent-first, multi-tenant business APIs billed per call. First service: CRM (companies, contacts, deals, notes). ## Use it from an agent - MCP endpoint: POST /mcp (streamable HTTP, JSON responses). Auth: `Authorization: Bearer `. - REST + OpenAPI 3.1: GET /openapi.json. Tools and operations share names: list_contacts, get_contact, create_contact, update_contact, delete_contact (same for companies, deals, notes). - OAuth: client credentials, and OAuth 2.1 authorization code + PKCE (S256) with dynamic client registration (POST /oauth/register) for Claude.ai/ChatGPT connectors. Discovery: /.well-known/oauth-protected-resource and /.well-known/oauth-authorization-server; 401s carry WWW-Authenticate resource_metadata. Scopes: crm:read, crm:write, admin. Quickstarts: docs/CONNECTORS.md. - Sandbox: POST /admin/sandbox (operator) creates a workspace with seed companies, contacts, deals and notes. - Errors: RFC 7807 application/problem+json with `code` and a `hint` saying how to fix it. - Writes: send `Idempotency-Key` to make retries safe (24 h replay window). - Pagination: `limit` (≤200) + `cursor` (pass back `next_cursor`). Search: `search` on companies/contacts. - Limits: RateLimit-Limit / RateLimit-Remaining / RateLimit-Reset headers; on 429 wait Retry-After seconds. - Health: GET /health, GET /status. Metrics: GET /metrics (operator token). Unsupported methods return 405 with Allow. - Encryption: contact emails and note bodies are encrypted at rest; filter contacts by exact email (?email=), but emails and note bodies are not substring-searchable. - NUL bytes in any input are rejected with 400. - Bulk: POST /v1/{entity}/bulk {op: create|update|delete} (max 100, atomic); MCP bulk_create_*/bulk_update_*/bulk_delete_*. - Filters on list: deals stage, amount_min, amount_max, contact_id; contacts company_id, email; notes contact_id; companies domain; all: created_after, created_before. Unknown filters are a 400. - Revocation: a revoked API key, OAuth client or token stops working within 60 s, including any cache (today: immediately). ## Data model company(name, domain) ← contact(name, email, company_id) ← deal(title, amount_cents, stage: lead|qualified|proposal|won|lost, contact_id); note(contact_id, body). References must point at records in your own workspace; otherwise 422 reference_not_found. ## Tenancy, export and deletion Every record is scoped to one workspace (tenant) by Postgres row-level security; credentials pick the tenant, never the request body. Every tenant-scoped table/blob is listed in packages/core/src/stores.ts; a test fails if a table lacks tenant_id or registration. Export: POST /v1/exports or MCP export_workspace (async); GET /v1/exports/{id} or get_export gives signed download links that expire after 15 minutes. Bundle: JSON + CSV per table + manifest.json with sha256 (docs/EXPORT.md). Per-record: GET /v1/{entity}/{id}/export (MCP export_), DELETE /v1/{entity}/{id}. Workspace hard delete: POST /v1/tenant/deletion -> confirm_token (valid 15 minutes) -> POST /v1/tenant/deletion/confirm; optional grace_hours 0..720; returns a deletion receipt. Neon backup (point-in-time restore) history keeps deleted data for up to 6 hours after the purge. - Rate limits: per tenant 600/min, per key 300/min, pre-auth per IP 30/min; 429 + RateLimit-* + Retry-After. - Contacts search covers names only when encryption is on; an email-looking search returns 400 search_unavailable (use filter email= for exact match). Legacy plaintext: scripts/backfill-encrypt.mjs.