# Human CRM — API

Human is an MCP- and API-first CRM for AI agents. Data is stored as portable
markdown "memories" attached to lightweight contacts, indexed for hybrid
(semantic + keyword) search, and queryable in natural language.

Base URL: `https://humancrm.com`
Endpoints are reachable at `/api/<endpoint>` (e.g. `/api/signup`). The explicit
`/api/memories/<endpoint>` form also works and is identical. Send/return JSON.

## Authentication

Every request uses a workspace bearer key:

```
Authorization: Bearer hc_xxx      # full access (read + write)
Authorization: Bearer hcr_xxx     # read-only
```

### Agents can sign up on their own — no human required

```
POST /api/signup
{ "name": "Acme relationships" }
→ { "workspaceId": "...", "apiKey": "hc_...", "docs": ".../docs/api.md" }
```

The key is shown once — store it. To hand the account to a human later, send a
claim link (they take ownership via an email magic link):

```
POST /api/account/claim
Authorization: Bearer hc_...
{ "email": "person@company.com" }
```

## Data model

- **Contact** (`entity`): a person, company, or deal. A `name` plus light,
  filterable `fields` (email, org, stage, value, tags…). Everything nuanced
  lives in markdown, not columns.
- **Memory**: a markdown note attached to a contact (or workspace-level). Each
  save is an immutable new version — full history is kept.
- **Summary**: one rolling markdown summary per contact.

## Contacts

```
GET  /api/entities?type=person&limit=50
POST /api/entities            { "type":"person", "name":"Dana Lee", "fields":{"email":"dana@acme.co"} }
GET  /api/entities/{id}       → { entity, documents }
PATCH/api/entities/{id}       { "name"?, "fields"?, "archived"? }
```

## Memories & summaries

```
POST /api/entities/{id}/notes      { "title":"Met at dinner", "markdown":"Dana mentioned..." }
GET  /api/entities/{id}/notes      → documents
GET  /api/entities/{id}/summary    → { summary, documentId }
PUT  /api/entities/{id}/summary    { "markdown":"..." }
GET  /api/documents/{id}           → { document, markdown }
PUT  /api/documents/{id}           { "markdown":"..." }   # saves a new version
GET  /api/documents/{id}/history   → versions
```

## Search

Hybrid semantic + keyword search over all memories.

```
POST /api/search   { "query":"who did we meet at the conference?", "entityId"?, "limit"? }
→ { hits: [{ chunkId, documentId, entityId, content, score }], entityIds }
```

## Custom queries (AI-extracted, cached — build your own read APIs)

Ask a question and get **structured data** the model extracts from the memories.
Optionally force the shape with a JSON Schema. This effectively lets you define
custom read APIs over your corpus.

```
POST /api/query
{
  "query": "every contact with a mailing address",
  "outputSchema": { "type":"array", "items":{ "type":"object",
     "properties":{ "name":{"type":"string"}, "address":{"type":"string"} } } },
  "slug": "addresses",      // optional: save as a reusable custom API
  "name": "Mailing addresses"
}
→ { result, cached, slug }
```

- **Caching**: results are cached per (query + schema). A named query is served
  from cache until it goes stale, then recomputed on next read.
- **Custom API endpoint**: a saved (slugged) query is callable directly:
  `GET /api/q/addresses` → cached-or-recomputed result.
- **Force refresh**: `POST /api/query?force=1`.
- **Cacheable fields / freshness**: the cache tracks which contacts a result
  depended on **and** the query's meaning. When a record gains new memories, only
  the queries that are *relevant* — by dependency or by semantic match — are
  invalidated and recomputed. Newly-relevant records (e.g. a brand-new contact
  with an address) are picked up automatically.

## Share a no-login view

Mint a signed, expiring link a human can open in a browser — no account.

```
POST /api/view-keys
{ "scope": { "kind":"entity", "entityId":"..." }, "ttlHours": 24 }
→ { "url": "https://humancrm.com/view/<token>" }
```

Scope kinds: `workspace` (directory), `entity` (one contact), `query` (a saved query's live result).

## Errors

JSON `{ "error": "message" }` with HTTP status (401 auth, 403 read-only,
404 not found, 422 validation, 429 rate limit).
