# Dorsal HTTP API reference

Base URL: `$DORSAL_API_URL` (default `https://api.dorsal.us`). Every request needs
`Authorization: Bearer <key>`; `scripts/dorsal.sh` adds it from `DORSAL_API_KEY` or `~/.dorsal/api-key`. POST bodies are JSON with
`Content-Type: application/json`. `GET /v1` returns this list with each body's JSON schema.

| Method | Path | Purpose |
|---|---|---|
| POST | `/v1/summaries` | Search. Ranked events, named objects and documentation chunks. |
| POST | `/v1/set` | Full records for exact ids, at most 25 per call. |
| POST | `/v1/neighbors` | Relationships of one named object, optionally its events. |
| GET | `/v1/protocols/dorsal?mode=investigate` | Method for a "dorsal …" request. Modes: investigate, improve, audit. |
| GET | `/v1/protocols/decision-verification` | Method for checking a decision. |

## POST /v1/summaries

| Field | Type | Notes |
|---|---|---|
| `query` | string, required | Topic, task, model, constraint or use-case phrase. |
| `total` | integer 1–100 | Items per kind. Default 50; start with 10. Up to 3 × total records. |
| `page_size` | integer 1–25 | Turns on paging. The response carries `next_cursor` while more remain. |
| `cursor` | string | `next_cursor` from the previous page. Repeat `query` and filters unchanged. |
| `event_types` | string[] | Filters events only. `capability_observed` = measured model results. A legacy name returns 400 naming its approved replacement; any other unknown name returns 400 listing the approved types. |
| `object_types` | string[] | Filters objects: `product_or_service`, `method`, `problem`, `use_case`, `repo`, `organization`, `person`. |
| `sources` | string[] | Filters events and docs: `docs`, `github`, `github_issue`, `github_pr`, `github_release`, `github_doc`, `reddit_post`, `blog_post`, `youtube_video`, `discord_thread`, `twitter_thread`, `podcast_transcript`, `podcast_episode`, `academic_paper`, `hackernews_thread`, `web`, `web_page`, `case_study`, `forum_post`, `press_release`, `status_page`. |
| `since`, `until` | ISO date | Bounds event and dated-documentation evidence. Objects are unfiltered. |

Event types: `decision_made`, `insight_discovered`, `action_taken`, `outcome_appeared`,
`problem_discovered`, `problem_solved`, `workaround_shared`, `capability_changed`,
`capability_observed`, `goal_set`, `idea_or_plan_proposed`, `information_source_discovered`,
`funding_event`, `job_change`, `partnership_announced`, `event_announced`, `product_released`,
`product_updated`, `release_published`, `pr_opened`, `pr_merged`, `pr_closed`, `issue_filed`, `issue_closed`,
`reddit_post`, `doc_added`, `doc_removed`, `doc_renamed`, `other`.

Response: `{ query, applied_filters, by_kind, total_returned, results: [...] }`. Each result has `id`,
`kind` (`event`, `object`, `documentation_chunk`), `description`, and for events and docs `url`,
`timestamp` and `source_type`. A `capability_observed` event adds `capability_observation_summary`.

## POST /v1/set

`{ "ids": ["<id>", ...] }`, 1 to 25 ids from `summaries` or `neighbors`. Returns
`{ requested, found, results }`. An unknown id comes back as `{ "id": "...", "found": false }`.
Ids are opaque labels: pass them back unchanged. Older event ids contain a type name that may no longer match the
event; read the type from `event_type`.
Events carry `source_url`, `raw_body` and the reported details; documentation chunks carry the full
passage; a `capability_observed` event carries the full measurement record.

## POST /v1/neighbors

| Field | Type | Notes |
|---|---|---|
| `query` | string, required | Object name or alias, or an event or documentation chunk id. |
| `include_events` | boolean | Adds the object's source-linked events. |
| `hops` | 1, 2 or 3 | Default 1. |
| `direction` | `out`, `in`, `both` | Default `both`. |
| `total` | integer 1–100 | Per section. Default 50. |
| `target_type`, `rel_type` | string[] | Narrow connected objects or relationship types. |
| `event_types`, `sources`, `since`, `until` | as above | Filter attached events when `include_events` is true. |

An unknown name returns `{ "query": "...", "error": "no object matched" }` with HTTP 200.

## Errors

| Status | `error.type` | What to do |
|---|---|---|
| 400 | `invalid_request_error` | Read the message; it names the field and the fix. |
| 401 | `authentication_error` | Key missing, wrong or expired. New key: https://godorsal.com/docs/skill. |
| 404 | `not_found_error` | Wrong path. The message lists the endpoints. |
| 405 | `method_not_allowed_error` | Wrong method. The `Allow` header names the right one. |
| 413 | `request_too_large` | Body over 2 MB. |
| 429 | `rate_limit_error` | Over 150 requests a minute on a trial key. Retry after `Retry-After` seconds. |
| 503 | `overloaded_error` | Busy or starting. Retry after `Retry-After` seconds. |
| 500 | `api_error` | Server failure. Report it. |
