Reference

HTTP API reference

Every FastAPI endpoint Personify exposes — request shape, response shape, curl examples.

The Personify HTTP API is a FastAPI app served by Uvicorn. npm start runs it at http://127.0.0.1:18765 and serves the UI at http://localhost:18766. An auto-generated Swagger UI lives at /docs.

The core endpoints are grouped here by purpose. The API has no authentication — keep it on 127.0.0.1 or behind a reverse proxy. See self-hosting for production guidance.

All POST bodies are JSON. All responses are JSON unless noted. The UI also uses /api/... routes for exports, vaults, embeddings, repo intake, runs, items, and MCP status; Swagger at /docs is the generated source of truth.


Health

GET /health

Liveness check. Returns immediately, does not touch the database.

Response

json
{ "status": "ok" }

Example

bash
curl http://localhost:18765/health

Sources & stats

GET /sources

List active sources registered in the database (i.e. sources that have at least one ingested item or registered export).

Response

json
[
  { "slug": "chatgpt",  "label": "ChatGPT",  "created_at": "2026-04-12T08:14:00Z" },
  { "slug": "gmail",    "label": "Gmail",    "created_at": "2026-04-12T09:01:00Z" }
]

Example

bash
curl http://localhost:18765/sources

GET /stats

Aggregate counts: items, exports, runs, and per-source / per-account breakdowns.

Response

json
{
  "items": 128402,
  "exports": 12,
  "runs": 14,
  "items_per_source": { "gmail": 82140 },
  "items_per_account": { "myname@example.com": 82140 },
  "sources": [{ "slug": "gmail", "label": "Gmail" }],
  "accounts": [{ "handle": "myname@example.com", "display_name": null }]
}

Example

bash
curl http://localhost:18765/stats

POST /search

Full-text search over item_text.body using Postgres' built-in FTS. Best for keyword and phrase queries.

Request

json
{
  "query":  "rust borrow checker",
  "limit":  25,
  "source": "github"
}
FieldTypeDefaultNotes
querystringrequiredSearch expression.
limitint25Maximum hits returned.
sourcestringoptionalRestrict to a single source slug.

Response

json
[
  {
    "id": 4127,
    "source": "github",
    "account": "my-org",
    "kind": "code",
    "ts": "2025-11-04T12:14:00Z",
    "title": "src/borrow.rs",
    "snippet": "fn check_borrow(...) ...",
    "score": 0.412
  }
]

Example

bash
curl -X POST http://localhost:18765/search \
  -H "Content-Type: application/json" \
  -d '{"query":"rust borrow checker","limit":10,"source":"github"}'

POST /semantic-search

pgvector cosine similarity search over embedded chunks. Requires the optional embeddings extra and a vault embed pass.

Request

json
{
  "query":  "the talk where someone explained ownership with a library metaphor",
  "limit":  25,
  "source": null
}

Same shape as /search. The query is embedded with the same model used to embed your items.

Response

Same shape as /search, but vector results include distance and chunk_idx instead of score (lower distance = more similar).

Example

bash
curl -X POST http://localhost:18765/semantic-search \
  -H "Content-Type: application/json" \
  -d '{"query":"library metaphor for ownership","limit":10}'

Items & timeline

GET /items/{id}

Retrieve a single item with its full text body, media references, and tags.

Response

json
{
  "id":      4127,
  "source":  "github",
  "account": "my-org",
  "kind":    "code",
  "ts":      "2025-11-04T12:14:00Z",
  "title":   "src/borrow.rs",
  "body":    "fn check_borrow(...) { ... }",
  "media":   [],
  "tags":    [{ "key": "topic", "value": "rust" }],
  "metadata": { "repo": "my-org/borrowctl", "path": "src/borrow.rs" }
}

Example

bash
curl http://localhost:18765/items/4127

GET /timeline

Items with non-null timestamps, in a date range, most recent first.

Query parameters

ParamTypeDescription
startISO 8601 datetimeInclusive lower bound.
endISO 8601 datetimeExclusive upper bound.
sourcestringRestrict to a source slug.
limitintDefault 200.

Response

json
[
  { "id": 4127, "source": "github", "account": "my-org", "kind": "code", "ts": "2025-11-04T12:14:00Z", "title": "src/borrow.rs" }
]

Example

bash
curl "http://localhost:18765/timeline?start=2025-11-01&end=2025-12-01&source=github"

Graph

GET /graph/entities/search

Search entities by name or alias substring.

Query parameters

ParamTypeDescription
qstringRequired. Substring matched against name, canonical_name, and aliases.
typestringOptional. One of the 22 entity types.
limitintDefault 20.

Response

json
[
  { "id": 81, "type": "Project", "name": "Personify", "canonical_name": "personify" }
]

Example

bash
curl "http://localhost:18765/graph/entities/search?q=personify&type=Project"

POST /graph/entities

Create an entity manually (most are extracted automatically during ingest).

Request

json
{
  "type": "Project",
  "name": "Personify",
  "aliases": ["personify", "the vault"]
}

Response — the created entity.

Example

bash
curl -X POST http://localhost:18765/graph/entities \
  -H "Content-Type: application/json" \
  -d '{"type":"Project","name":"Personify"}'

GET /graph/entities/{id}

Get an entity with all its aliases and item-grounded evidence.

Response

json
{
  "entity": { "id": 81, "type": "Project", "name": "Personify", "canonical_name": "personify" },
  "aliases": [{ "id": 9, "alias": "the vault", "normalized_alias": "the vault", "source": null }],
  "evidence": [
    { "id": 22, "source_type": "item", "source_id": 4127, "quote": "Personify is a local-first ..." }
  ]
}

GET /graph/entities/{id}/neighborhood

Walk the graph outward from an entity. depth is capped at 2.

Query parameters

ParamTypeDescription
depthint1 (default) or 2.

Response

json
{
  "center": { "id": 81, "type": "Project", "name": "Personify" },
  "nodes": [{ "id": 81, "type": "Project", "name": "Personify" }],
  "edges": [{ "source_entity_id": 81, "target_entity_id": 14, "relationship_type": "USES" }]
}

GET /graph/entities/{id}/context

LLM-friendly grounding payload: the entity, its neighborhood, evidence snippets, and suggested follow-up queries. Designed to be dropped into a prompt.

Response

json
{
  "entity": { "...": "..." },
  "related_entities": [],
  "relationships": [],
  "evidence": [{ "item_id": 4127, "snippet": "..." }],
  "suggested_queries": ["how does Personify ingest GitHub repos?"]
}

POST /graph/relationships

Create a relationship.

Request

json
{
  "source_entity_id": 81,
  "target_entity_id": 14,
  "relationship_type": "USES"
}

relationship_type is one of the 18 enumerated types — see architecture.

Response — the created relationship.


Vault management

GET /api/vaults

Return the active vault plus discovered vault profiles.

bash
curl http://localhost:18765/api/vaults

POST /api/vaults

Create and initialize a named vault. This creates the Postgres database inside the existing Docker container, creates the filesystem root under ./vaults/, applies schema, seeds parser sources, and optionally activates it.

bash
curl -X POST http://localhost:18765/api/vaults \
  -H "Content-Type: application/json" \
  -d '{"name":"work","activate":true}'

POST /api/vaults/{name}/activate

Switch the running process to an existing vault.

bash
curl -X POST http://localhost:18765/api/vaults/work/activate

MCP control

GET /api/mcp/status

Return whether the UI-gated HTTP MCP transport is enabled.

bash
curl http://localhost:18765/api/mcp/status

POST /api/mcp/start

Open the HTTP MCP gate mounted at /mcp.

POST /api/mcp/stop

Close the HTTP MCP gate. When closed, /mcp returns 503.


Schema

The Swagger UI at http://localhost:18765/docs is the live, generated truth. The shapes shown above are the stable surface; if you're writing a client, generate it from the OpenAPI document at /openapi.json.