Review API
The review index, annotations with verdicts (including bulk), comments and threaded replies, and suggestions with accept/reject.
All paths are relative to https://docs.aicomputercompany.com/api. Background: Review markup (RFM).
Review index
GET /docs/:id/review-index
Read everything review-related in one call.
Response — 200:
{
"source": "live",
"fileVersion": "rev_…",
"index": { "items": [ "…all review items with line numbers…" ] },
"annotations": [
{ "id": "a1", "line": 12, "verdict": "conflict", "refs": ["a7"], "body": "…" }
]
}
sourceis"live"or"revision"depending on what was indexed.index.itemslists all review items (annotations, comments, suggestions) with line numbers.annotationslists only the items carrying a verdict.
Annotations
Annotations are Layer 1: spec-review verdicts anchored to exact text.
Create an annotation
POST /docs/:id/annotations
Body:
| Field | Type | Required | Description |
|---|---|---|---|
anchorText | string | yes | Exact text in the document to anchor to (outside existing markup). |
occurrence | number | no (default 1) | Disambiguates repeated anchor text. |
verdict | string | yes | "valid" | "question" | "conflict". |
body | string | yes | Your explanation. |
refs | string[] | no | Ids of conflicting annotations (for conflict). |
checkpoint | boolean | no (default false) | Create a revision for this single annotation. |
message | string | no | Revision message. |
Response — 201: {"id": "a1", "line": 12, "revision": null | {…}}
Errors — 409 if anchorText is not found outside existing markup.
Verdict meanings: valid = requirement is clear & consistent · question = needs clarification · conflict = conflicts with another requirement (use refs).
Bulk annotations
POST /docs/:id/annotations/bulk
The primary verb for a review pass. One request, one revision, however many annotations.
Body:
| Field | Type | Required | Description |
|---|---|---|---|
annotations | array | yes | Same shape as single-annotation create. |
checkpoint | boolean | no (default true) | One revision for the whole pass. |
message | string | no | e.g. "AI spec review". |
Response — 201:
{
"created": [{ "id": "a1", "…": "…" }],
"failed": [{ "input": { "…": "…" }, "error": "…" }],
"revision": { "…": "…" }
}
Failed anchors do not abort the rest — check failed and retry those with corrected anchors.
curl -X POST https://docs.aicomputercompany.com/api/docs/doc_abc123/annotations/bulk \
-H 'Content-Type: application/json' -H 'x-actor-id: agent_demo' \
-d '{"annotations": [
{"anchorText": "must support offline mode", "verdict": "valid",
"body": "Confirmed in section 3.2"},
{"anchorText": "sync every 5 minutes", "verdict": "conflict",
"body": "Conflicts with the realtime requirement", "refs": ["a1"]}
]}'
Resolve / delete an annotation
POST /docs/:id/annotations/:targetId/resolve body: {"resolution"?: "…"}
DELETE /docs/:id/annotations/:targetId
Both return {"ok": true, "revision": {…}}. Resolve marks status=resolved in the endmatter; delete removes the markup and the endmatter entry.
Comments & replies
Comments are Layer 2: verdict-less discussion, threaded and resolvable.
Create a comment
POST /docs/:id/comments
Body:
| Field | Type | Required | Description |
|---|---|---|---|
body | string | yes | Comment text. |
anchorText | string | no | Exact text to anchor to. Omit (with occurrence) for a document-level comment. |
occurrence | number | no | Disambiguates repeated anchor text. |
Response — 201: {"id": "c1" | null, "revision": {…}}
Reply / resolve
POST /docs/:id/comments/:parentId/replies body: {"body": "reply text"}
→ 201 {"ok": true, "revision": {…}} threaded under the parent
POST /docs/:id/comments/:targetId/resolve body: {"resolution"?: "…"}
→ 200 {"ok": true, "revision": {…}}
curl -X POST https://docs.aicomputercompany.com/api/docs/doc_abc123/comments \
-H 'Content-Type: application/json' -H 'x-actor-id: agent_demo' \
-d '{"anchorText": "launch in Q3", "body": "Is this still the target after the slip?"}'
Suggestions
Tracked changes humans accept or reject in the editor, stored as CriticMarkup in the document text: {++added++}, {--deleted--}, {~~old~>new~~}.
Create a suggestion
POST /docs/:id/suggestions
Body:
| Field | Type | Required | Description |
|---|---|---|---|
kind | string | yes | "addition" | "deletion" | "substitution". |
anchorText | string | for addition | Insert after this text. |
targetText | string | for deletion / substitution | Text to delete or replace. |
occurrence | number | no | Disambiguates repeated text. |
text | string | for addition / substitution | The new text. |
body | string | no | Rationale shown in the review rail. |
Response — 201: {"id": "s1", "revision": {…}}
Errors — 409 if anchorText/targetText is not found.
curl -X POST https://docs.aicomputercompany.com/api/docs/doc_abc123/suggestions \
-H 'Content-Type: application/json' \
-d '{"kind": "substitution", "targetText": "5 minutes",
"text": "30 seconds", "body": "Match the SLA in section 2"}'
Accept / reject
POST /docs/:id/suggestions/:targetId/accept → {"ok": true, "revision": {…}}
POST /docs/:id/suggestions/:targetId/reject → {"ok": true, "revision": {…}}
Accept strips the marker and keeps the new text; reject mirrors that. Anyone with the document id (human or agent) can accept or reject.