---
title: "Record a decision on an intent from outside the editor conversation — a call made in a PR review, a Slack thread, or by an agent mid-build. Appends to the intent's decision audit trail (what was chosen, what was ruled out, why, and what would reopen it)."
method: POST
path: "/api/v1/intents/{id}/decisions"
tags: ["decisions"]
---

# Record a decision on an intent from outside the editor conversation — a call made in a PR review, a Slack thread, or by an agent mid-build. Appends to the intent's decision audit trail (what was chosen, what was ruled out, why, and what would reopen it).

`POST /api/v1/intents/{id}/decisions`

Rate-limited by the 'general' bucket (300/h sliding window, keyed user:<userId> or ip:<ip>). The append is an optimistic-concurrency compare-and-swap fenced on the intent's updated_at (up to 3 attempts), so two agents posting simultaneously cannot overwrite each other's decision; a persistently lost race returns 409. Appends beyond the 100-decision cap are rejected with 409 rather than silently discarded by the sanitizer. The merged set is sanitized (malformed legacy rows dropped) before persisting. The server assigns id (UUID) and resolvedAt (epoch ms). evidenceIds are validated against the workspace's evidence_items and non-resolving ids are dropped; the field is omitted when none resolve. Emits the server-side 'decision_recorded' analytics event with source 'api'.

## Path parameters

- `id` string, required

## Query parameters

- `workspace_id` string

## Request body

- object
  - `choice` string, required — What was chosen. Non-empty after trim, max 500 chars.
  - `reason` string, required — Why — the evidence or reasoning behind the call. Non-empty after trim, max 1000 chars.
  - `ruledOut` string — The alternative that was ruled out. Non-empty after trim, max 500 chars.
  - `reopenTrigger` string — What new evidence or change would reopen this decision. Non-empty after trim, max 500 chars.
  - `evidenceIds` string[] — Evidence item UUIDs backing the decision (max 20). Ids are verified against the workspace's evidence_items; ids that don't resolve are silently dropped so a caller cannot fabricate provenance.

## Response `201`

Created — returns { intentId, decision, decisionCount }

- object

## Other responses

- `400` — Invalid request body (zod) — { error, details }; OR cookie-session auth missing workspace_id
- `401` — Invalid/expired API key, or no Bearer key and no valid session cookie
- `403` — Lacks write scope; OR cookie auth not a member of the workspace
- `404` — Intent not found within this workspace
- `409` — Decision log at capacity (100) — remove or supersede an existing decision first; OR persistent concurrent updates to the intent (retry the request)
- `429` — Rate limit exceeded (general bucket, 300/h) — { error } with Retry-After header
- `500` — DB error recording the decision or unexpected internal error

---

[API](https://skmtc.dev/pathmode/apis/pathmode-api.md) · [All operations](https://skmtc.dev/pathmode/apis/pathmode-api/llms.txt) · [OpenAPI document](https://skmtc-service-production.skmtc.workers.dev/v1/apis/pathmode/pathmode-api/revisions/e419abb0a327/schema)
