Concepts
How authentication, review content, policies, and workflows fit together.
Authentication
| Actor | How | Notes |
|---|---|---|
| Agents / backends | x-api-key: ak_… or Authorization: Bearer ak_… |
Scopes: admin or consumer |
| Dashboard users | Cognito email/password → bearer access token (or x-access-token) |
Roles: admin or consumer |
| Email reviewers | Personal link with auth code (rrc_…) |
No account; exchange via POST /v1/review/exchange |
Admin can do everything consumer can. Check the current principal with
GET /v1/me.
API keys & users
API keys
- Admin — manage users and keys; also create and read review requests.
- Consumer — create and read review requests only. Prefer this for agents.
Secrets are returned once on create/signup/rotate (ak_…). Hashed at rest. Admin endpoints:
POST /v1/api-keys—{ "label", "scope?" }(defaultconsumer)GET /v1/api-keys— list active keys (no secret)POST /v1/api-keys/{keyId}/revokePOST /v1/api-keys/{keyId}/rotate— revokes and issues a new admin key
Users
GET /v1/users/POST /v1/users— list / invite (email, optionalname,role)PATCH /v1/users/{userId}— set roleDELETE /v1/users/{userId}— cannot delete yourself or the last admin
Documents
Each review item has a document descriptor:
{
"type": "url" | "image" | "pdf" | "text" | "html",
"url": "https://…", // typical for url / image / pdf
"title": "optional title",
"content": "inline body…", // typical for text / html
"metadata": { }
}
type is required. Other fields are optional at the schema level — send what your asset needs
(for example a public URL for previews, or inline content for copy).
External IDs & metadata
externalId is an optional string on requests and items
(/^[A-Za-z0-9._:-]+$/, 1–128 chars. Reuse the same ID across reviews over time (same PR, asset,
or deploy target). List endpoints return newest first.
GET /v1/review-requests?filter[externalId]=landing-page-v2
GET /v1/review-items?filter[externalId]=preview-asset-1
MCP equivalents: list_review_requests with external_id;
list_review_items requires external_id.
metadata is an opaque key-value bag on requests, items, comments, and documents. Stored and
returned; not searchable.
Subscribers
Optional subscribers on create — email addresses notified when the request reaches a verdict
(approved, rejected, or changes_required. Set at creation only.
Reviewers decide; subscribers are notified of the result. They are not the same role.
Multi-item reviews
One request can include several assets. Prefer decisionScope: "item" so reviewers approve or
reject each item independently.
"policy": { "type": "ALL_APPROVE", "decisionScope": "item" },
"items": [
{ "title": "Landing page", "document": { "type": "url", "url": "https://…" } },
{ "title": "Creative PDF", "document": { "type": "pdf", "url": "https://…" } }
]
Request-level status rolls up items: pending if any pending; rejected if any rejected;
changes_required if any need changes; otherwise approved. Detail/list responses include
evaluationScope, outcome, and often itemOutcomes.
Policies
"policy": {
"type": "ALL_APPROVE" | "ANY_APPROVE" | "ANY_REJECT" | "QUORUM",
"quorum": 2, // QUORUM only; defaults to reviewer count
"rejectionThreshold": 1, // optional early reject after N rejections
"decisionScope": "request" | "item" // default "request"
}
| Type | Behavior |
|---|---|
ALL_APPROVE |
Every reviewer must approve; any rejection rejects. Pending while anyone is undecided. |
ANY_APPROVE |
First approval wins. Otherwise pending, then rejection / changes if no approvals remain. |
ANY_REJECT |
First rejection wins. Otherwise pending, then changes / approval. |
QUORUM |
Approvals ≥ quorum → approved. If remaining non-rejected reviewers cannot reach quorum
→ rejected.
|
If rejectionThreshold is set, reaching that many rejections rejects immediately for all policy
types. cancelled / expired are terminal and not overwritten by policy.
Status & outcomes
Request / item status:
pending, approved, rejected,
changes_required, cancelled, expired.
List and detail responses include outcome:
{
"status": "pending",
"summary": "0/2 approved · 2 awaiting",
"tally": {
"approved": 0,
"rejected": 0,
"changes_required": 0,
"pending": 2
}
}
For item-scoped reviews, outcome.tally counts items; use outcome.summary for mixed
results such as 1 approved · 1 rejected. Comment status is open or
resolved.
Create a review request
POST /v1/review-requests · MCP create_review_request
Body fields
| Field | Required | Description |
|---|---|---|
title | yes | Request title |
description | no | Longer context for reviewers |
items | yes (≥1) | Assets to review — see below |
reviewers | yes (≥1) | { email, name? } |
subscribers | no | Emails notified on verdict |
policy | yes | See Policies |
externalId | no | Your correlation ID |
metadata | no | Opaque bag |
Item fields
| Field | Required | Description |
|---|---|---|
title | yes | Item title |
document | yes | See Documents |
id | no | Client-supplied item id |
externalId | no | Item correlation ID |
metadata | no | Opaque bag |
Example
curl -X POST "https://rest.agentfabric.dev/v1/review-requests" \
-H "content-type: application/json" \
-H "x-api-key: $AGENTFABRIC_API_KEY" \
-d '{
"title": "Approve landing page",
"description": "Draft for next week’s launch",
"externalId": "landing-page-v2",
"policy": { "type": "ALL_APPROVE", "decisionScope": "request" },
"reviewers": [{ "email": "reviewer@example.com", "name": "Alex" }],
"subscribers": ["ops@example.com"],
"items": [
{
"title": "Preview",
"externalId": "preview-asset-1",
"document": { "type": "url", "url": "https://example.com/page" },
"metadata": { "branch": "main" }
}
],
"metadata": { "env": "staging" }
}'
Full request/response schemas: POST /v1/review-requests and Resources.
Reviewers
Each reviewer gets an emailed personal link
{REVIEW_APP_URL}/review?authCode=… — no account. Add, remind, or cancel via the
review request endpoints (or MCP
add_reviewer / remind_reviewers / cancel_review_request).
agentfabric.dev
Comments
Reviewers create comments; agents list and resolve them. See ReviewComment and comment endpoints.