Getting Started
Connect your AI coding agent or your own tooling to your Pathmode workspace — intents, evidence, and product context. Two surfaces share one workspace: the MCP server for coding agents, and the REST API for everything else. You can be up and running in under a minute.
1. Get your API key
In the app, go to Settings → API Keys, create a key, and copy it — it is shown only once. You can also create keys programmatically with POST /api-keys. Keys are prefixed with pm_live_.
2. Install the MCP server
The fastest path is auto setup — it detects your MCP clients and writes their config for you:
npx @pathmode/mcp-server@latest setup pm_live_...Or add the server manually to your MCP client config (e.g. claude_desktop_config.json or .mcp.json):
{
"mcpServers": {
"pathmode": {
"command": "npx",
"args": ["@pathmode/mcp-server"],
"env": {
"PATHMODE_API_KEY": "pm_live_..."
}
}
}
}The package also ships the Pathmode skill pack. Install it into .claude/skills/ with:
npx @pathmode/mcp-server@latest install-skills # project
npx @pathmode/mcp-server@latest install-skills --global # user-wide3. Make your first request
Confirm everything works with one call. Fetch your workspace — strategy, products, and constitution:
curl https://pathmode.io/api/v1/workspace \
-H "Authorization: Bearer pm_live_..."A successful call returns a workspace object:
{
"id": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d",
"name": "Acme Product",
"urlKey": "acme",
"strategy": { "vision": "The simplest way to manage product intent" },
"products": [{ "id": "7d8e9f0a-1b2c-4d3e-9f8a-6b5c4d3e2f1a", "name": "Checkout" }],
"constitutionRules": [
{ "id": "c4d3e2f1-a0b9-4c8d-7e6f-5a4b3c2d1e0f", "text": "All changes must be backwards-compatible", "isActive": true }
]
}That's a working integration. See Authentication for scopes and key management, then the REST API Reference for every endpoint.
Authentication
The API uses two authentication modes. Most resource endpoints accept a pm_live_ API key as a Bearer token. A few dashboard/admin endpoints authenticate only via a logged-in session cookie.
API key (Bearer)
Most endpoints accept a pm_live_ key in the Authorization header (“Bearer pm_live_…”). The key carries its own workspace, so no workspace_id is needed. These same endpoints also accept a logged-in session cookie — in which case you must pass a workspace_id query parameter.
Session cookie only
A few dashboard/admin endpoints — the api-keys management endpoints, outcome measurements, and codebase analysis — authenticate only via a logged-in Supabase session cookie. A pm_live_ API key is rejected with 401 on these routes; they are documented for completeness and for first-party / browser use.
Base URL
https://pathmode.io/api/v1Example request
curl https://pathmode.io/api/v1/workspace \
-H "Authorization: Bearer pm_live_..."When authenticating a resource endpoint with a session cookie instead of a key, pass a workspace_id query parameter so the request knows which workspace to target. With an API key this is inferred from the key.
Scopes
API keys carry one or more scopes. Provision the least privilege a key needs — a key without the required scope gets 403. Each endpoint in the reference lists the scope it requires.
| Scope | Grants |
|---|---|
| read | Read-only access — all GET endpoints (intents, evidence, workspace, constitution, export, prompts, notes). |
| write | Everything read can do, plus creating and mutating intents, evidence, notes, links, status transitions, share tokens, and AI verification. |
| admin | Reserved for workspace owners/admins. Required for key management and codebase analysis (session-authenticated routes). |
API Key Lifecycle
Keys can be managed in the dashboard or over the api-keys endpoints (session-authenticated; owner/admin only). A created key's secret is returned exactly once — store it immediately. To rotate, create a new key, update your clients, then delete the old one.
# Create a scoped key (returns the secret once)
curl -X POST https://pathmode.io/api/v1/api-keys \
-H "Content-Type: application/json" \
-b "sb-access-token=<your session>" \
-d '{ "name": "CI pipeline key", "workspaceId": "<workspaceId>", "scopes": ["read", "write"] }'
# Revoke a key when rotating out
curl -X DELETE https://pathmode.io/api/v1/api-keys \
-H "Content-Type: application/json" \
-b "sb-access-token=<your session>" \
-d '{ "keyId": "<keyId>" }'REST API Reference
All 25 endpoints of the v1 API. Prefer a machine-readable spec? Download the OpenAPI 3.1 document to import into Postman, Insomnia, or an SDK generator.
Intents
Create, read, update, and transition intent specs through their lifecycle, plus AI grading and agent-ready prompts.
/api/v1/intentsreadList all intents for the authenticated workspace, optionally filtered by status, ordered by updated_at descending.
Query Parameters
| Name | Type | Description |
|---|---|---|
status | string | Optional exact-match filter on intent status (e.g. draft, validated, approved, shipped, verified). Passed straight to an .eq('status', ...) filter. |
workspace_id | string | Required ONLY when authenticating via session cookie (no pm_live_ Bearer). Identifies which workspace the cookie session targets; ignored for API-key auth (key carries its own workspace). |
Request
curl -X GET https://pathmode.io/api/v1/intents \
-H "Authorization: Bearer pm_live_..."Response
{
"intents": [
{
"id": "3f2a9c1e-8b4d-4e2a-9c1e-7a6b5c4d3e2f",
"workspaceId": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d",
"productId": "7d8e9f0a-1b2c-4d3e-9f8a-6b5c4d3e2f1a",
"stageName": null,
"status": "draft",
"version": 1,
"title": "Speed up onboarding",
"objective": "Cut time-to-first-value for new workspaces under 10 minutes.",
"problemSeverity": "high",
"outcomes": [
{ "id": "c4d3e2f1-a0b9-4c8d-7e6f-5a4b3c2d1e0f", "text": "New user reaches first intent in under 10 min", "priority": "must" }
],
"healthMetrics": ["activation_rate"],
"scope": null,
"strategicAlignment": null,
"alignmentNotes": null,
"context": {},
"constraints": ["No new third-party signup steps"],
"verification": {},
"externalLinks": [],
"evidenceAnchors": {},
"implementationContext": null,
"createdAt": "2026-06-01T12:00:00.000Z",
"updatedAt": "2026-06-05T09:30:00.000Z",
"shippedAt": null,
"verifiedAt": null,
"edgeCases": [],
"evidenceIds": [],
"relations": [
{ "targetId": "9e8d7c6b-5a4f-4e3d-2c1b-0a9f8e7d6c5b", "type": "depends_on" }
]
}
],
"count": 1
}List path uses INTENT_LIST_SELECT which does NOT join edge cases or evidence: edgeCases and evidenceIds always come back as empty arrays [] here (populate them via the detail endpoint GET /api/v1/intents/:id). relations IS populated on the list path. Rows that fail apiIntentSchema.parse are silently skipped (logged), so count may be less than the raw row count. Default order: updated_at descending. No rate limiting on this GET.
Status Codes
200 OK — returns { intents, count }400 Cookie-session auth only: workspace_id query param missing401 Invalid/expired API key, or no Bearer key and no valid session cookie403 Authenticated but lacks read scope (or, for cookie auth, not a member of the workspace)500 DB error fetching intents or unexpected internal error/api/v1/intentswriteRate limit: intents 200/hCreate a new intent spec (status defaults to draft) under a product in the authenticated workspace.
Query Parameters
| Name | Type | Description |
|---|---|---|
workspace_id | string | Required ONLY for cookie-session auth; identifies the target workspace. Ignored for API-key auth. |
Request Body
| Name | Type | Description |
|---|---|---|
titlerequired | string | Non-empty after trim. Stored as user_goal. |
objectiverequired | string | Non-empty after trim. |
productIdrequired | string | Non-empty after trim. Must reference a product in this workspace or returns 404. |
outcomes | Array<string | { id?: string; text: string; priority?: 'must'|'should'|'could' }> | Strings or structured objects; strings must be non-empty after trim. Normalized to StructuredOutcome[] at the boundary. |
constraints | string[] | Coerced to trimmed non-empty strings. |
healthMetrics | string[] | Coerced to trimmed non-empty strings. |
edgeCases | Array<{ scenario: string; expectedBehavior: string }> | Each field non-empty after trim. Synced via replaceIntentEdgeCasesRpc; on failure the just-created intent is deleted and 500 returned. |
verification | { e2eTests?: string[]; unitTests?: string[]; manualChecks?: string[] } | Verification plan object; defaults to {} if omitted. |
problemSeverity | 'low' | 'medium' | 'high' | 'critical' | Optional severity enum. |
scope | { inScope?: string[]; outOfScope?: string[] } | null | Optional scope object; may be null. |
Request
curl -X POST https://pathmode.io/api/v1/intents \
-H "Authorization: Bearer pm_live_..." \
-H "Content-Type: application/json" \
-d '{
"title": "Speed up onboarding",
"objective": "New users drop off before reaching first value.",
"productId": "7d8e9f0a-1b2c-4d3e-9f8a-6b5c4d3e2f1a"
}'Response
{
"id": "3f2a9c1e-8b4d-4e2a-9c1e-7a6b5c4d3e2f",
"workspaceId": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d",
"productId": "7d8e9f0a-1b2c-4d3e-9f8a-6b5c4d3e2f1a",
"stageName": null,
"status": "draft",
"version": 1,
"title": "Speed up onboarding",
"objective": "Cut time-to-first-value for new workspaces under 10 minutes.",
"problemSeverity": "high",
"outcomes": [
{ "id": "c4d3e2f1-a0b9-4c8d-7e6f-5a4b3c2d1e0f", "text": "New user reaches first intent in under 10 min", "priority": "must" }
],
"healthMetrics": ["activation_rate"],
"scope": null,
"strategicAlignment": null,
"alignmentNotes": null,
"context": {},
"constraints": ["No new third-party signup steps"],
"verification": {},
"externalLinks": [],
"evidenceAnchors": {},
"implementationContext": null,
"createdAt": "2026-06-07T10:15:00.000Z",
"updatedAt": "2026-06-07T10:15:00.000Z",
"shippedAt": null,
"verifiedAt": null,
"edgeCases": [
{ "id": "b2c1d0e9-f8a7-4b6c-5d4e-3f2a1b0c9d8e", "scenario": "User has no products yet", "expectedBehavior": "Show empty-state prompt" }
],
"evidenceIds": [],
"relations": []
}Rate-limited by the 'intents' bucket (200/h sliding window, keyed user:<userId> or ip:<ip>). Billing-gated: when the workspace intent quota is reached, returns 403 with body { error: 'plan_limit_reached', metric: 'intents', current, limit, plan }. New intent always starts at status 'draft', version 1. Fires a Slack 'intent_status_changed' notification and an 'intent_created' analytics event (both fire-and-forget). Response is built from a re-fetch with relations (INTENT_WITH_RELATIONS_SELECT) so edgeCases are populated; evidenceIds empty on a fresh create.
Status Codes
201 Created — returns the full mapped intent400 Invalid request body (zod) — returns { error, details } with flattened zod errors; OR cookie-session auth missing workspace_id401 Invalid/expired API key, or no Bearer key and no valid session cookie403 Lacks write scope; OR plan_limit_reached (billing intents quota exceeded); OR cookie auth not a member of the workspace404 Product not found in this workspace429 Rate limit exceeded (intents bucket, 200/h) — { error } with Retry-After header500 Insert error, edge-case sync failure (intent rolled back/deleted), or unexpected internal error/api/v1/intents/:idreadGet a single intent with edge cases, linked evidence ids, and relations.
Query Parameters
| Name | Type | Description |
|---|---|---|
workspace_id | string | Required ONLY for cookie-session auth; identifies the target workspace. Ignored for API-key auth. |
Request
curl -X GET https://pathmode.io/api/v1/intents/3f2a9c1e-8b4d-4e2a-9c1e-7a6b5c4d3e2f \
-H "Authorization: Bearer pm_live_..."Response
{
"id": "3f2a9c1e-8b4d-4e2a-9c1e-7a6b5c4d3e2f",
"workspaceId": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d",
"productId": "7d8e9f0a-1b2c-4d3e-9f8a-6b5c4d3e2f1a",
"stageName": null,
"status": "shipped",
"version": 3,
"title": "Speed up onboarding",
"objective": "Cut time-to-first-value for new workspaces under 10 minutes.",
"problemSeverity": "high",
"outcomes": [
{ "id": "c4d3e2f1-a0b9-4c8d-7e6f-5a4b3c2d1e0f", "text": "New user reaches first intent in under 10 min", "priority": "must" }
],
"healthMetrics": ["activation_rate"],
"scope": { "inScope": ["Signup flow"], "outOfScope": ["Billing"] },
"strategicAlignment": null,
"alignmentNotes": null,
"context": {},
"constraints": ["No new third-party signup steps"],
"verification": { "manualChecks": ["New user can create an intent in under 10 min"] },
"externalLinks": [],
"evidenceAnchors": { "outcome:0": ["d3e2f1a0-b9c8-4d7e-6f5a-4b3c2d1e0f9a"] },
"implementationContext": null,
"createdAt": "2026-06-01T12:00:00.000Z",
"updatedAt": "2026-06-06T14:20:00.000Z",
"shippedAt": "2026-06-06T14:20:00.000Z",
"verifiedAt": null,
"edgeCases": [
{ "id": "b2c1d0e9-f8a7-4b6c-5d4e-3f2a1b0c9d8e", "scenario": "User has no products yet", "expectedBehavior": "Show empty-state prompt" }
],
"evidenceIds": ["d3e2f1a0-b9c8-4d7e-6f5a-4b3c2d1e0f9a"],
"relations": [
{ "targetId": "9e8d7c6b-5a4f-4e3d-2c1b-0a9f8e7d6c5b", "type": "depends_on" }
]
}Detail path uses INTENT_WITH_RELATIONS_SELECT, so edgeCases, evidenceIds (from intent_spec_evidence.friction_id), and relations are all fully populated here (unlike the list endpoint). Scoped to the authenticated workspace via .eq('workspace_id', ...). No rate limiting on this GET.
Status Codes
200 OK — returns the full mapped intent400 Cookie-session auth only: workspace_id query param missing401 Invalid/expired API key, or no Bearer key and no valid session cookie403 Lacks read scope; OR cookie auth not a member of the workspace404 Intent not found (Supabase PGRST116) within this workspace500 DB error or unexpected internal error/api/v1/intents/:idwriteUpdate intent content fields (title, objective, outcomes, constraints, etc.). Does NOT change status — use the /status endpoint.
Query Parameters
| Name | Type | Description |
|---|---|---|
workspace_id | string | Required ONLY for cookie-session auth; identifies the target workspace. Ignored for API-key auth. |
Request Body
| Name | Type | Description |
|---|---|---|
title | string | Non-empty after trim if provided. Stored as user_goal. |
objective | string | Non-empty after trim if provided. |
outcomes | Array<string | { id?: string; text: string; priority?: 'must'|'should'|'could' }> | Replaces both outcomes_v2 and the flat outcomes text list. |
constraints | string[] | Replaces constraints (coerced to trimmed non-empty strings). |
healthMetrics | string[] | Replaces health_metrics (coerced to trimmed non-empty strings). |
edgeCases | Array<{ scenario: string; expectedBehavior: string }> | Replace-all semantics via replaceIntentEdgeCasesRpc; on failure the intent is rolled back to its prior snapshot and 500 returned. |
verification | { e2eTests?: string[]; unitTests?: string[]; manualChecks?: string[] } | Replaces verification plan. |
problemSeverity | 'low' | 'medium' | 'high' | 'critical' | null | Nullable severity enum. |
evidenceAnchors | Record<string, string[]> | Map of anchor key (e.g. 'objective', 'outcome:0', 'edgeCase:<uuid>') to evidence id arrays. |
scope | { inScope?: string[]; outOfScope?: string[] } | null | Nullable scope object. |
Request
curl -X PATCH https://pathmode.io/api/v1/intents/3f2a9c1e-8b4d-4e2a-9c1e-7a6b5c4d3e2f \
-H "Authorization: Bearer pm_live_..."Response
{
"id": "3f2a9c1e-8b4d-4e2a-9c1e-7a6b5c4d3e2f",
"workspaceId": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d",
"productId": "7d8e9f0a-1b2c-4d3e-9f8a-6b5c4d3e2f1a",
"stageName": null,
"status": "draft",
"version": 1,
"title": "Speed up onboarding (v2)",
"objective": "Cut time-to-first-value for new workspaces under 8 minutes.",
"problemSeverity": "critical",
"outcomes": [
{ "id": "c4d3e2f1-a0b9-4c8d-7e6f-5a4b3c2d1e0f", "text": "New user reaches first intent in under 8 min", "priority": "must" }
],
"healthMetrics": ["activation_rate", "time_to_first_intent"],
"scope": null,
"strategicAlignment": null,
"alignmentNotes": null,
"context": {},
"constraints": ["No new third-party signup steps"],
"verification": { "manualChecks": ["New user creates an intent under 8 min"] },
"externalLinks": [],
"evidenceAnchors": { "objective": ["d3e2f1a0-b9c8-4d7e-6f5a-4b3c2d1e0f9a"] },
"implementationContext": null,
"createdAt": "2026-06-01T12:00:00.000Z",
"updatedAt": "2026-06-07T11:00:00.000Z",
"shippedAt": null,
"verifiedAt": null,
"edgeCases": [
{ "id": "b2c1d0e9-f8a7-4b6c-5d4e-3f2a1b0c9d8e", "scenario": "User has no products yet", "expectedBehavior": "Show empty-state prompt" }
],
"evidenceIds": ["d3e2f1a0-b9c8-4d7e-6f5a-4b3c2d1e0f9a"],
"relations": []
}Content-only update; status/shipped_at/verified_at are NOT touched here. Always sets updated_at. Requires at least one updatable field (otherwise 400). When edgeCases is provided, the prior intent state is snapshotted first so a failed edge-case sync can roll back. Response is re-fetched with INTENT_WITH_RELATIONS_SELECT so edgeCases/evidenceIds/relations are fully populated. No rate limiting on this PATCH.
Status Codes
200 OK — returns the full updated intent (re-fetched with relations)400 Invalid request body (zod) — { error, details }; OR no updatable field provided ('At least one field must be provided to update'); OR cookie-session auth missing workspace_id401 Invalid/expired API key, or no Bearer key and no valid session cookie403 Lacks write scope; OR cookie auth not a member of the workspace404 Intent not found (PGRST116) within this workspace (on snapshot or update)500 DB error, edge-case sync failure (intent rolled back to prior snapshot), or unexpected internal error/api/v1/intents/:id/statuswriteUpdate an intent's status (draft/validated/approved/shipped/verified), managing shipped_at/verified_at timestamps and returning a verification checklist for shipped/verified transitions.
Query Parameters
| Name | Type | Description |
|---|---|---|
workspace_id | string | Required ONLY for cookie-session auth; identifies the target workspace. Ignored for API-key auth. |
Request Body
| Name | Type | Description |
|---|---|---|
statusrequired | 'draft' | 'validated' | 'approved' | 'shipped' | 'verified' | Target status. draft/validated/approved clear shipped_at and verified_at; shipped sets shipped_at and clears verified_at; verified sets verified_at. |
verificationResult | { status: 'shipped'|'verified'; items: Array<{ text: string; category: 'outcome'|'constitution'|'health_metric'; checked: boolean }>; note?: string; checkedCount: number; totalCount: number; completedAt: number } | Optional. When present it is saved to verification_result and a summary is logged as an intent_implementation_notes row (source 'verification_checkpoint'). |
Request
curl -X PATCH https://pathmode.io/api/v1/intents/3f2a9c1e-8b4d-4e2a-9c1e-7a6b5c4d3e2f/status \
-H "Authorization: Bearer pm_live_..." \
-H "Content-Type: application/json" \
-d '{
"status": "shipped"
}'Response
{
"id": "3f2a9c1e-8b4d-4e2a-9c1e-7a6b5c4d3e2f",
"status": "shipped",
"updatedAt": "2026-06-07T12:00:00.000Z",
"shippedAt": "2026-06-07T12:00:00.000Z",
"verifiedAt": null,
"verificationChecklist": [
{ "text": "New user reaches first intent in under 10 min", "category": "outcome" },
{ "text": "activation_rate", "category": "health_metric" },
{ "text": "New user can create an intent in under 10 min", "category": "manual_check" },
{ "text": "Never ship a flow that adds a new required signup field", "category": "constitution" }
],
"verificationChecklistCount": 4
}Base response is always { id, status, updatedAt, shippedAt, verifiedAt }. ONLY when status is 'shipped' or 'verified' does it add verificationChecklist (items {text, category} drawn from outcomes [category 'outcome'], health_metrics ['health_metric'], verification.manualChecks ['manual_check'], and active constitution_rules ['constitution']) and verificationChecklistCount. Fires a Slack 'intent_status_changed' notification (fire-and-forget). No billing gate, no rate limiting on this endpoint.
Status Codes
200 OK — returns { id, status, updatedAt, shippedAt, verifiedAt }, plus verificationChecklist + verificationChecklistCount for shipped/verified transitions400 Invalid request body (zod) — { error, details }; OR cookie-session auth missing workspace_id401 Invalid/expired API key, or no Bearer key and no valid session cookie403 Lacks write scope; OR cookie auth not a member of the workspace404 Intent not found (PGRST116) within this workspace500 DB error updating status or unexpected internal error/api/v1/intents/:id/verifywriteAI-grade an implementation against the intent spec. Evaluates each outcome, constraint, constitution rule, and edge case via Gemini structured output; stores the result on the intent and logs a verification_checkpoint implementation note.
Request Body
| Name | Type | Description |
|---|---|---|
summaryrequired | string | Implementation summary the AI grades against the spec. Trimmed, must be non-empty. |
codeChanges | string | Optional code-changes blob; first 15000 chars are included in the AI prompt. |
Request
curl -X POST https://pathmode.io/api/v1/intents/3f2a9c1e-8b4d-4e2a-9c1e-7a6b5c4d3e2f/verify \
-H "Authorization: Bearer pm_live_..." \
-H "Content-Type: application/json" \
-d '{
"summary": "Implemented the 3-step onboarding flow with inline validation."
}'Response
{
"pass": false,
"score": 72,
"results": [
{
"item": "Users can reset password from the login screen",
"category": "outcome",
"status": "pass",
"reasoning": "The summary describes a reset link added to the login screen wired to the existing reset flow."
},
{
"item": "No PII is written to logs",
"category": "constitution",
"status": "unclear",
"reasoning": "The summary does not mention logging behavior, so compliance cannot be determined."
}
],
"summary": "The core reset flow is implemented and matches the primary outcome. Logging and one edge case are unaddressed, lowering confidence."
}category enum is outcome|constraint|constitution|edge_case; status enum is pass|fail|unclear. pass is true only when zero items fail; unclear counts as half credit toward score (0-100). Side effects: writes verification_result + updated_at on intent_specs, and inserts an intent_implementation_notes row with source 'verification_checkpoint'. No rate limiter on this route despite being AI-backed.
Status Codes
200 Verification graded; returns the VerificationResult object400 Invalid request body (zod) — e.g. missing/empty summary401 Missing/invalid API key or session403 Authenticated but lacks write scope404 Intent not found in this workspace503 AI verification service temporarily unavailable (Gemini API error / 429 / 503)500 Internal server error/api/v1/intents/:id/promptreadGenerate the formatted agent prompt for an intent. Assembles spec + workspace strategy + product + constitution rules + linked evidence and returns both a markdown/string prompt and a structured JSON payload.
Query Parameters
| Name | Type | Description |
|---|---|---|
agent_type | string | Target agent: cursor|windsurf|claude-code|codex|generic. Default 'claude-code'. |
mode | string | draft|execute. Default 'execute'. In 'execute' mode the intent must pass readiness (meaningful title + objective + >=1 outcome) or 422. |
format | string | json|markdown. Default 'markdown'. Shapes the prompt string. |
workspace_id | string | Required only when authenticating via session cookie (ignored for API-key auth). |
Request
curl -X GET https://pathmode.io/api/v1/intents/3f2a9c1e-8b4d-4e2a-9c1e-7a6b5c4d3e2f/prompt \
-H "Authorization: Bearer pm_live_..."Response
{
"prompt": "# Intent: Add password reset\n\n## Objective\nLet users recover access without contacting support...",
"json": {
"title": "Add password reset",
"objective": "Let users recover access without contacting support.",
"outcomes": ["Users can reset password from the login screen"],
"constraints": [],
"edgeCases": [],
"constitutionRules": ["[BLOCKING] No PII in logs"]
}
}json is an opaque object whose exact shape is produced by generateAgentPrompt (varies by agent_type/format) — fields above are illustrative. The 422 body is { error, readiness: { isSubstantive, isReadyForExecution, score, blockers[], warnings[] }, suggestion }. implementation_context is stripped from the export unless the workspace flag implementation_context_enabled is on. Constitution rules are prefixed [BLOCKING]/[advisory] by enforcement level. Date fields (createdAt/updatedAt/shippedAt/verifiedAt) are converted to epoch ms internally before prompt generation.
Status Codes
200 Returns { prompt, json }401 Missing/invalid API key or session403 Authenticated but lacks read scope404 Intent not found in this workspace422 mode=execute but intent is not ready for execution; body includes { error, readiness, suggestion }500 Internal server error/api/v1/intents/:id/sharewriteGenerate (or return existing) a share token for the intent, used to build public review links. Idempotent: returns the existing token if one is already set.
Query Parameters
| Name | Type | Description |
|---|---|---|
workspace_id | string | Required only when authenticating via session cookie (ignored for API-key auth). |
Request
curl -X POST https://pathmode.io/api/v1/intents/3f2a9c1e-8b4d-4e2a-9c1e-7a6b5c4d3e2f/share \
-H "Authorization: Bearer pm_live_..."Response
{
"token": "7a6b5c4d-3e2f-4a1b-9c8d-2e1f0a9b8c7d"
}token is a raw crypto.randomUUID() (v4 UUID, no prefix), generated by reviewService.generateShareToken and persisted to intent_specs.share_token. No 201 — created and pre-existing tokens both return 200. No rate limiter on this route.
Status Codes
200 Returns { token } (existing or newly generated)401 Missing/invalid API key or session403 Authenticated but lacks write scope404 Intent not found in this workspace500 Failed to generate share link/api/v1/intents/:id/sharewriteRevoke the intent's share token, invalidating all existing review links (sets intent_specs.share_token to null).
Query Parameters
| Name | Type | Description |
|---|---|---|
workspace_id | string | Required only when authenticating via session cookie (ignored for API-key auth). |
Request
curl -X DELETE https://pathmode.io/api/v1/intents/3f2a9c1e-8b4d-4e2a-9c1e-7a6b5c4d3e2f/share \
-H "Authorization: Bearer pm_live_..."Response
{
"success": true
}Revocation is unconditional once ownership is verified — succeeds even if no token currently exists. No rate limiter on this route.
Status Codes
200 Share token revoked; returns { success: true }401 Missing/invalid API key or session403 Authenticated but lacks write scope404 Intent not found in this workspace500 Failed to revoke share linkImplementation Notes
Record and read the technical decisions made while implementing an intent.
/api/v1/intents/:id/noteswriteRate limit: general 300/hLog an implementation note against an intent.
Query Parameters
| Name | Type | Description |
|---|---|---|
workspace_id | string | Required ONLY for cookie-session auth; identifies the target workspace. Ignored for API-key auth. |
Request Body
| Name | Type | Description |
|---|---|---|
noterequired | string | Non-empty after trim. |
source | 'mcp' | 'cli' | 'github' | 'manual' | 'verification_checkpoint' | 'linear' | 'jira' | 'execution_sync' | Origin of the note; defaults to 'manual' when omitted. |
Request
curl -X POST https://pathmode.io/api/v1/intents/3f2a9c1e-8b4d-4e2a-9c1e-7a6b5c4d3e2f/notes \
-H "Authorization: Bearer pm_live_..." \
-H "Content-Type: application/json" \
-d '{
"note": "Chose progressive disclosure over a wizard — fewer clicks for power users."
}'Response
{
"id": "e1f2a3b4-c5d6-4e7f-8a9b-0c1d2e3f4a5b",
"intentId": "3f2a9c1e-8b4d-4e2a-9c1e-7a6b5c4d3e2f",
"note": "Shipped behind flag; rollout to 10% of workspaces.",
"source": "cli",
"createdAt": "2026-06-07T13:00:00.000Z"
}Rate-limited by the 'general' bucket (300/h sliding window, keyed user:<userId> or ip:<ip>). The created row's created_by is set to the authenticated userId, but the POST response does NOT echo createdBy (only id, intentId, note, source, createdAt). The GET list response DOES include createdBy.
Status Codes
201 Created — returns { id, intentId, note, source, createdAt }400 Invalid request body (zod) — { error, details }; OR cookie-session auth missing workspace_id401 Invalid/expired API key, or no Bearer key and no valid session cookie403 Lacks write scope; OR cookie auth not a member of the workspace404 Intent not found within this workspace429 Rate limit exceeded (general bucket, 300/h) — { error } with Retry-After header500 DB error creating note or unexpected internal error/api/v1/intents/:id/notesreadList implementation notes for an intent, newest first.
Query Parameters
| Name | Type | Description |
|---|---|---|
workspace_id | string | Required ONLY for cookie-session auth; identifies the target workspace. Ignored for API-key auth. |
Request
curl -X GET https://pathmode.io/api/v1/intents/3f2a9c1e-8b4d-4e2a-9c1e-7a6b5c4d3e2f/notes \
-H "Authorization: Bearer pm_live_..."Response
{
"notes": [
{
"id": "e1f2a3b4-c5d6-4e7f-8a9b-0c1d2e3f4a5b",
"intentId": "3f2a9c1e-8b4d-4e2a-9c1e-7a6b5c4d3e2f",
"note": "Shipped behind flag; rollout to 10% of workspaces.",
"source": "cli",
"createdBy": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d",
"createdAt": "2026-06-07T13:00:00.000Z"
}
],
"count": 1
}Default order: created_at descending (newest first). Each note includes createdBy (unlike the POST create response, which omits it). No rate limiting on this GET.
Status Codes
200 OK — returns { notes, count }400 Cookie-session auth only: workspace_id query param missing401 Invalid/expired API key, or no Bearer key and no valid session cookie403 Lacks read scope; OR cookie auth not a member of the workspace404 Intent not found within this workspace500 DB error fetching notes or unexpected internal errorOutcome Measurements
Track measured values against an intent’s outcomes after shipping. Session-authenticated (dashboard) endpoints.
/api/v1/intents/:id/outcomesFetch all outcome measurements recorded for an intent.
Request
Session-authenticated endpoint — sign in via the dashboard; a pm_live_ API key is rejected here.
curl -X GET https://pathmode.io/api/v1/intents/3f2a9c1e-8b4d-4e2a-9c1e-7a6b5c4d3e2f/outcomes \
-H "Cookie: sb-access-token=<your session>"Response
{
"measurements": [
{
"id": "9c1e7a6b-5c4d-4e2a-8b4d-3f2a9c1e7a6b",
"intentId": "3f2a9c1e-8b4d-4e2a-9c1e-7a6b5c4d3e2f",
"outcomeIndex": 0,
"outcomeText": "Password reset completion rate",
"baselineValue": "42%",
"actualValue": "68%",
"unit": "%",
"met": true,
"source": "manual",
"recordedBy": "d3e2f1a0-9b8c-4d7e-8f6a-5b4c3d2e1f0a",
"measuredAt": "2026-06-07T12:34:56.000Z"
}
]
}Cookie-session only — uses createServerClient + auth.getUser(); does NOT inspect the Authorization header and rejects pm_live_ API keys. No workspace_id param and no explicit workspace-membership/scope check on this route (relies on Supabase RLS for the session user). Default sort: outcome_index ascending, then measured_at descending. baselineValue/unit may be null; met may be null.
Status Codes
200 Returns { measurements } ordered by outcome_index asc, then measured_at desc401 No valid Supabase session cookie (Unauthorized)500 Failed to fetch measurements / internal server error/api/v1/intents/:id/outcomesRecord a new outcome measurement for an intent.
Request Body
| Name | Type | Description |
|---|---|---|
outcomeIndexrequired | number | Index of the outcome being measured. Required (rejected only when undefined/null, so 0 is valid). |
outcomeTextrequired | string | Outcome text snapshot. Required non-empty string; trimmed before insert. |
actualValuerequired | string | Measured value. Required non-empty string; trimmed before insert. |
baselineValue | string | Baseline value before the change. Stored null if falsy. |
unit | string | Unit of measure. Stored null if falsy. |
met | boolean | Whether the outcome target was met. Stored null if not provided (uses ?? null). |
source | string | Provenance label. Defaults to 'manual' when falsy. |
Request
Session-authenticated endpoint — sign in via the dashboard; a pm_live_ API key is rejected here.
curl -X POST https://pathmode.io/api/v1/intents/3f2a9c1e-8b4d-4e2a-9c1e-7a6b5c4d3e2f/outcomes \
-H "Cookie: sb-access-token=<your session>" \
-H "Content-Type: application/json" \
-d '{
"outcomeIndex": 0,
"outcomeText": "New user reaches first intent in under 10 min",
"actualValue": "8 min median"
}'Response
{
"id": "9c1e7a6b-5c4d-4e2a-8b4d-3f2a9c1e7a6b",
"intentId": "3f2a9c1e-8b4d-4e2a-9c1e-7a6b5c4d3e2f",
"outcomeIndex": 0,
"outcomeText": "Password reset completion rate",
"baselineValue": "42%",
"actualValue": "68%",
"unit": "%",
"met": true,
"source": "manual",
"recordedBy": "d3e2f1a0-9b8c-4d7e-8f6a-5b4c3d2e1f0a",
"measuredAt": "2026-06-07T12:34:56.000Z"
}Cookie-session only — rejects pm_live_ API keys; no Authorization-header inspection. Manual validation (not zod): outcomeIndex rejected only when undefined/null so 0 passes; outcomeText/actualValue must be non-empty strings. recorded_by is set to the session user id; source defaults to 'manual'. workspace_id is derived from the intent row (no membership check beyond RLS). Inserts into intent_outcome_measurements.
Status Codes
201 Measurement recorded; returns the created measurement400 Missing required field — outcomeIndex (undefined/null), outcomeText, or actualValue401 No valid Supabase session cookie (Unauthorized)404 Intent not found500 Failed to record measurement / internal server errorEvidence
Create and search evidence items, and link them to intents for traceability from user problem to shipped solution.
/api/v1/intents/:id/evidencewriteRate limit: general 300/hLink and/or unlink evidence items to/from an intent. Dual-writes the intent_spec_evidence join table and the linked_intent_ids array on evidence_items.
Request Body
| Name | Type | Description |
|---|---|---|
link | string[] | Evidence item UUIDs to link. Validated against the workspace; unknown IDs silently skipped. At least one of link/unlink must be a non-empty array. |
unlink | string[] | Evidence item UUIDs to unlink. Validated against the workspace; unknown IDs silently skipped. At least one of link/unlink must be a non-empty array. |
Request
curl -X POST https://pathmode.io/api/v1/intents/3f2a9c1e-8b4d-4e2a-9c1e-7a6b5c4d3e2f/evidence \
-H "Authorization: Bearer pm_live_..."Response
{
"intentId": "3f2a9c1e-8b4d-4e2a-9c1e-7a6b5c4d3e2f",
"evidenceIds": ["a1b2c3d4-5e6f-4a7b-8c9d-0e1f2a3b4c5d", "b2c3d4e5-6f7a-4b8c-9d0e-1f2a3b4c5d6e"],
"linked": 2,
"unlinked": 0
}evidenceIds in the response is re-fetched from intent_spec_evidence after applying link/unlink, so it reflects ALL currently-linked evidence (not just what was changed). link/unlink counts only increment for IDs that exist in the workspace. Cookie-session auth requires ?workspace_id= and a workspace membership.
Status Codes
200 Link/unlink applied; returns counts and the intent's full current evidence ID list400 Invalid request body (zod) — e.g. neither link nor unlink is a non-empty array401 Missing/invalid API key or session403 Authenticated but lacks write scope404 Intent not found in this workspace429 general rate limit exceeded (300/h per user)500 Internal server error/api/v1/evidencereadQuery active evidence items for the authenticated workspace, with filtering and pagination.
Query Parameters
| Name | Type | Description |
|---|---|---|
productId | string (uuid) | Filter to a single product. |
type | string | Filter by evidence type: friction | quote | observation | metric | request. 400 if invalid. |
severity | string | Filter by severity: low | medium | high | critical. 400 if invalid. |
search | string | Case-insensitive ILIKE substring match on content. |
tags | string | Comma-separated tag list; matches rows whose tags array contains ALL given tags. |
limit | integer | Page size, default 50, capped at 200. |
offset | integer | Pagination offset, default 0. |
workspace_id | string (uuid) | Required ONLY for cookie-session auth (no Bearer key); ignored for API-key auth. Without it, cookie auth returns 400. |
Request
curl -X GET https://pathmode.io/api/v1/evidence \
-H "Authorization: Bearer pm_live_..."Response
{
"evidence": [
{
"id": "3f2a9c1e-8b4d-4e2a-9c1e-7a6b5c4d3e2f",
"productId": "b1c2d3e4-f5a6-4789-b0c1-2d3e4f5a6b7c",
"workspaceId": "7a6b5c4d-3e2f-4a1b-9c8d-0e1f2a3b4c5d",
"type": "friction",
"content": "Users abandon checkout when the address form rejects valid postal codes.",
"source": "Support ticket #4821",
"sourceUrl": "https://support.example.com/t/4821",
"severity": "high",
"sentiment": "negative",
"tags": ["checkout", "forms"],
"stage": "Checkout",
"linkedIntentIds": ["c9d8e7f6-a5b4-4321-9876-543210fedcba"],
"createdAt": "2026-06-01T10:15:30.000Z",
"updatedAt": "2026-06-02T08:00:00.000Z"
}
],
"count": 1,
"limit": 50,
"offset": 0
}List path. Filters on status='active' only. Default sort created_at DESC. 'count' is the exact total matching the filters (count: 'exact'), falling back to the returned page length if null. Every field is always present in the object (nullable columns serialize as null; tags/linkedIntentIds coalesce to []).
Status Codes
200 Evidence list returned.400 Invalid type/severity enum value, or (cookie auth) missing workspace_id.401 Missing/invalid API key or no valid session cookie.403 API key lacks read scope; or (cookie auth) not a member of the workspace.500 DB or internal error./api/v1/evidencewriteRate limit: general 300/hCreate a new evidence item under a product in the authenticated workspace.
Query Parameters
| Name | Type | Description |
|---|---|---|
workspace_id | string (uuid) | Required ONLY for cookie-session auth (no Bearer key); ignored for API-key auth. |
Request Body
| Name | Type | Description |
|---|---|---|
contentrequired | string | Non-empty evidence text; trimmed before storage. |
typerequired | string | One of friction | quote | observation | metric | request. |
productIdrequired | string (uuid) | Product must belong to the workspace (else 404). |
source | string | Free-text source label; stored as null if omitted. |
sourceUrl | string | Source URL; stored as null if omitted. |
severity | string | low | medium | high | critical. 400 if invalid. |
sentiment | string | positive | negative | neutral | mixed. 400 if invalid. |
tags | string[] | Tag array; defaults to []. |
stage | string | Free-text journey stage; stored as null if omitted. |
Request
curl -X POST https://pathmode.io/api/v1/evidence \
-H "Authorization: Bearer pm_live_..." \
-H "Content-Type: application/json" \
-d '{
"content": "Users report confusion at the pricing toggle.",
"type": "friction",
"productId": "7d8e9f0a-1b2c-4d3e-9f8a-6b5c4d3e2f1a"
}'Response
{
"id": "d4e5f6a7-b8c9-4012-a345-6789bcdef012",
"productId": "b1c2d3e4-f5a6-4789-b0c1-2d3e4f5a6b7c",
"workspaceId": "7a6b5c4d-3e2f-4a1b-9c8d-0e1f2a3b4c5d",
"type": "friction",
"content": "Users abandon checkout when the address form rejects valid postal codes.",
"source": "Support ticket #4821",
"sourceUrl": "https://support.example.com/t/4821",
"severity": "high",
"sentiment": "negative",
"tags": ["checkout", "forms"],
"stage": "Checkout",
"linkedIntentIds": [],
"createdAt": "2026-06-07T12:00:00.000Z",
"updatedAt": "2026-06-07T12:00:00.000Z"
}Billing-gated: a 403 with body { error:'plan_limit_reached', metric:'evidence_items', current, limit, plan } is returned when the evidence_items quota is hit (distinct from the scope 403 which has body { error: 'Insufficient permissions...' }). Row stored with status='active', triage_status='inbox', created_by=auth.userId. Fires a Slack notify + PostHog evidence_created event (fire-and-forget). linkedIntentIds is [] on a fresh create.
Status Codes
201 Evidence created.400 Missing/empty content, missing/invalid type, missing productId, or invalid severity/sentiment.401 Missing/invalid API key or no valid session cookie.403 API key lacks write scope; cookie auth not a workspace member; OR billing limit reached (body { error: 'plan_limit_reached', metric: 'evidence_items', current, limit, plan }).404 productId not found in this workspace.429 Rate limit exceeded (general bucket, 300/h).500 DB or internal error.Codebase Analysis
Stream an AI analysis of the bound repository to synthesize Implementation Context for an intent. Beta, session-authenticated.
/api/v1/intents/:id/analyze-codebaseBetaadminRate limit: analyzeCodebase 30/24h (per workspace)Spin up a Vercel Sandbox, shallow-clone the bound GitHub repo, gather a spec-targeted codebase slice, and synthesize an Implementation Context via Gemini. Streams progress as NDJSON. Sole writer of intent_specs.implementation_context.
Request
Session-authenticated endpoint — sign in via the dashboard; a pm_live_ API key is rejected here.
curl -X POST https://pathmode.io/api/v1/intents/3f2a9c1e-8b4d-4e2a-9c1e-7a6b5c4d3e2f/analyze-codebase \
-H "Cookie: sb-access-token=<your session>"Response
{"phase":"cloning","message":"Cloning acme/payroll-web (default branch)"}
{"phase":"analyzing","message":"Synthesizing implementation context from 37 files"}
{"phase":"writing","message":"Persisting implementation context","partialResult":{"relevantAreas":[{"path":"src/auth/reset.ts","reason":"Holds the existing reset-token flow this intent extends"}],"currentBehavior":"Reset is admin-only via a CLI script.","liftEstimate":"medium","risks":["Token reuse if expiry is not enforced"],"verificationSuggestions":["Add an e2e test for the reset link flow"],"analyzedAt":"2026-06-07T12:34:56.000Z","analyzedRepo":{"provider":"github","owner":"acme","name":"payroll-web","ref":"main"},"analyzedSpecVersion":"a1b2c3d4e5f6a7b8"}}
{"phase":"done","message":"Analysis complete","updatedSpec":{"id":"3f2a9c1e-8b4d-4e2a-9c1e-7a6b5c4d3e2f","workspaceId":"7a6b5c4d-3e2f-4a1b-9c8d-2e1f0a9b8c7d","productId":"b2c3d4e5-6f7a-4b8c-9d0e-1f2a3b4c5d6e","status":"draft","version":3,"title":"Add password reset","objective":"Let users recover access without contacting support.","outcomes":[{"id":"o1","text":"Users can reset password from the login screen"}],"healthMetrics":[],"constraints":[],"verification":{},"externalLinks":[],"evidenceAnchors":{},"implementationContext":{"relevantAreas":[{"path":"src/auth/reset.ts","reason":"Holds the existing reset-token flow"}],"currentBehavior":"Reset is admin-only via a CLI script.","liftEstimate":"medium","risks":["Token reuse if expiry is not enforced"],"verificationSuggestions":["Add an e2e test for the reset link flow"],"analyzedAt":"2026-06-07T12:34:56.000Z","analyzedRepo":{"provider":"github","owner":"acme","name":"payroll-web","ref":"main"},"analyzedSpecVersion":"a1b2c3d4e5f6a7b8"},"createdAt":"2026-06-01T09:00:00.000Z","updatedAt":"2026-06-07T12:34:56.000Z","shippedAt":null,"verifiedAt":null,"edgeCases":[],"evidenceIds":[],"relations":[]}}Cookie-session only — uses createClient() from @/lib/supabase/server + auth.getUser(); does NOT accept pm_live_ API keys. Role gate requires owner|admin|editor membership (treated as admin scope). Double-gated: paid plan (pro/team — free is 403) AND workspace flag implementation_context_enabled. Response is streamed NDJSON, Content-Type 'application/x-ndjson; charset=utf-8', Cache-Control no-store. Each line is a ProgressEvent { phase, message, partialResult?, updatedSpec?, error? }; errors after the 200 surface as a {phase:'error'} line, not an HTTP error. Rate limit keyed by workspace:<id>, NOT user; example products (is_example) are exempt from the cap. analyzedSpecVersion is a 16-char sha256 of canonical spec content. updatedSpec is the full mapIntentRowToApiIntent shape.
Status Codes
200 Streaming NDJSON of ProgressEvent lines (cloning, analyzing, writing, done; or a final error line). HTTP 200 returned immediately before work completes.400 No GitHub repo/binding configured, missing installation, or no default repo for the binding401 No valid Supabase session (Authentication required)403 Not owner/admin/editor of the workspace; OR plan is free; OR implementation_context_enabled flag is off404 Intent not found429 Daily analysis limit reached for this workspace (30/24h); body { error, retryAfterSeconds } + Retry-After header503 Service not configured (Supabase service role key / URL missing)Workspace & Constitution
Read workspace strategy, products, and the constitution rules enforced during verification.
/api/v1/workspacereadGet workspace details: name, url key, tags, strategy, active products, and active constitution rules.
Query Parameters
| Name | Type | Description |
|---|---|---|
workspace_id | string (uuid) | Required ONLY for cookie-session auth (no Bearer key); ignored for API-key auth. |
Request
curl -X GET https://pathmode.io/api/v1/workspace \
-H "Authorization: Bearer pm_live_..."Response
{
"id": "7a6b5c4d-3e2f-4a1b-9c8d-0e1f2a3b4c5d",
"name": "Acme Product Team",
"urlKey": "acme-product",
"tags": ["beta"],
"strategy": null,
"products": [
{
"id": "b1c2d3e4-f5a6-4789-b0c1-2d3e4f5a6b7c",
"workspaceId": "7a6b5c4d-3e2f-4a1b-9c8d-0e1f2a3b4c5d",
"name": "Checkout",
"slug": "checkout",
"description": "The purchase flow.",
"productVision": "Frictionless checkout in under 30 seconds.",
"northStar": "Completed checkouts per week",
"targetAudience": "Returning shoppers",
"defaultContext": {},
"manifest": null,
"icon": "cart",
"color": "#3A8A8C",
"status": "active",
"createdBy": "e1f2a3b4-c5d6-4789-a012-3b4c5d6e7f80",
"createdAt": "2026-05-01T09:00:00.000Z",
"updatedAt": "2026-06-01T09:00:00.000Z"
}
],
"constitutionRules": [
{
"id": "a1b2c3d4-e5f6-4789-9012-3456789abcde",
"category": "privacy",
"text": "Never log raw PII.",
"rationale": "Compliance with GDPR.",
"scope": "all-products",
"enforcement": "required",
"examples": ["Mask email addresses in logs."],
"isActive": true,
"createdAt": "2026-04-10T11:00:00.000Z"
}
]
}strategy is read from workspaces.settings.strategy (null when unset). products only includes status='active' rows, sorted created_at ASC. Optional product columns (description/productVision/northStar/targetAudience/manifest/icon/color) are omitted from JSON when falsy (mapped to undefined); defaultContext defaults to {}. constitutionRules only includes is_active=true rows, sorted created_at DESC; rationale/scope/examples omitted when null/non-array; enforcement defaults to 'required'.
Status Codes
200 Workspace returned.400 Cookie auth missing workspace_id query param.401 Missing/invalid API key or no valid session cookie.403 API key lacks read scope; or cookie auth not a workspace member.404 Workspace not found.500 Products fetch failure or internal error./api/v1/workspace/constitutionreadGet the active constitution rules for the authenticated workspace.
Query Parameters
| Name | Type | Description |
|---|---|---|
workspace_id | string (uuid) | Required ONLY for cookie-session auth (no Bearer key); ignored for API-key auth. |
Request
curl -X GET https://pathmode.io/api/v1/workspace/constitution \
-H "Authorization: Bearer pm_live_..."Response
{
"rules": [
{
"id": "a1b2c3d4-e5f6-4789-9012-3456789abcde",
"category": "privacy",
"text": "Never log raw PII.",
"rationale": "Compliance with GDPR.",
"scope": "all-products",
"enforcement": "required",
"examples": ["Mask email addresses in logs."],
"isActive": true,
"createdAt": "2026-04-10T11:00:00.000Z"
}
],
"count": 1
}Only returns is_active=true rules, sorted created_at DESC. rationale/scope omitted when null; examples omitted when not an array; enforcement defaults to 'required'. count is rules.length.
Status Codes
200 Rules returned.400 Cookie auth missing workspace_id query param.401 Missing/invalid API key or no valid session cookie.403 API key lacks read scope; or cookie auth not a workspace member.500 DB or internal error.Context Export
Export workspace and intent context as CLAUDE.md, AGENTS.md, .cursorrules, an intent spec, or an outcome rubric.
/api/v1/exportreadExport workspace data as agent context files: CLAUDE.md, AGENTS.md, .cursorrules, or intent.md.
Query Parameters
| Name | Type | Description |
|---|---|---|
format | string | claude-md (default) | agents-md | cursorrules | intent-md. Any other value yields 400. |
intent_id | string (uuid) | For cursorrules/intent-md: target a specific intent; if omitted, pickBestIntent chooses one. For claude-md it influences product resolution. |
product_id | string (uuid) | For claude-md: include this product's manifest context (explicit override; else resolved from intent, else first active product). |
workspace_id | string (uuid) | Required ONLY for cookie-session auth (no Bearer key); ignored for API-key auth. |
Request
curl -X GET https://pathmode.io/api/v1/export \
-H "Authorization: Bearer pm_live_..."Response
# CLAUDE.md
# (success responses are NOT JSON — body is the generated markdown/plaintext file)
# Content-Type: text/markdown; charset=utf-8 (claude-md, agents-md, intent-md)
# Content-Type: text/plain; charset=utf-8 (cursorrules)
# Content-Disposition: attachment; filename="CLAUDE.md" | "AGENTS.md" | ".cursorrules" | "intent.md"
#
# JSON is only returned on error/422, e.g.:
# {
# "error": "Intent is not ready to export as agent context.",
# "readiness": { "isReadyForExecution": false, "isSubstantive": false },
# "suggestion": "Add a real objective and at least one concrete outcome before exporting agent rules."
# }SUCCESS RESPONSE IS A FILE DOWNLOAD, NOT JSON. claude-md sets Content-Type text/markdown + filename CLAUDE.md, filters out non-substantive intents (evaluateIntentReadiness.isSubstantive) and prepends a <!-- PATHMODE:WARNINGS --> block via getContextWarnings (plus a note when stub intents were filtered). agents-md is identical to claude-md (same agent-agnostic '# Pathmode Intent Context' body) but sets filename AGENTS.md — the native instruction file for OpenAI Codex and modern Cursor. cursorrules sets text/plain + filename .cursorrules. intent-md sets text/markdown + filename intent.md. Intents fetched via INTENT_WITH_RELATIONS_SELECT, sorted updated_at DESC; createdAt/updatedAt/shippedAt/verifiedAt are converted to epoch-millis numbers before generation. 422 and 404 bodies are JSON.
Status Codes
200 File generated; body is markdown/plaintext (see Content-Type), not JSON.400 Invalid format value; or cookie auth missing workspace_id.401 Missing/invalid API key or no valid session cookie.403 API key lacks read scope; or cookie auth not a workspace member.404 cursorrules/intent-md: requested intent_id not found, or no intents exist in the workspace.422 cursorrules/intent-md: target intent is not ready for execution (returns JSON { error, readiness, suggestion }).500 Internal error.API Keys
List, create, and revoke API keys. Session-authenticated (dashboard) endpoints — not callable with a pm_live_ key.
/api/v1/api-keysList the current user's API keys (key value never returned).
Request
Session-authenticated endpoint — sign in via the dashboard; a pm_live_ API key is rejected here.
curl -X GET https://pathmode.io/api/v1/api-keys \
-H "Cookie: sb-access-token=<your session>"Response
{
"keys": [
{
"id": "f1e2d3c4-b5a6-4978-8a90-1b2c3d4e5f60",
"workspaceId": "7a6b5c4d-3e2f-4a1b-9c8d-0e1f2a3b4c5d",
"name": "CI deploy key",
"keyPrefix": "pm_live_3f2a9c1e",
"scopes": ["read", "write"],
"lastUsedAt": "2026-06-06T18:22:10.000Z",
"expiresAt": null,
"createdAt": "2026-05-20T14:00:00.000Z"
}
],
"count": 1
}Cookie session only; uses createClient from @/lib/supabase/server + getUser, never inspects the Authorization header, so a pm_live_ Bearer token is NOT accepted. Lists keys for the authenticated user (eq user_id), sorted created_at DESC. The raw key is never returned — only keyPrefix. If admin client unavailable or api_keys table missing (code 42P01), returns 200 with { keys: [], count: 0, message }.
Status Codes
200 Keys returned (or empty list with a 'message' field if SUPABASE_SERVICE_ROLE_KEY/the api_keys table is not configured).401 No valid session cookie (rejects API keys — this route does NOT call authenticateApiRoute).429 Rate limit exceeded (apiKeysRead bucket, 120/h).500 DB or internal error./api/v1/api-keysadminRate limit: apiKeysWrite 20/hCreate a new API key for a workspace the user owns/admins; returns the secret key exactly once.
Request Body
| Name | Type | Description |
|---|---|---|
namerequired | string | Human label for the key. |
workspaceIdrequired | string (uuid) | Workspace the key belongs to; caller must be admin/owner. |
scopes | string[] | Subset of read | write | admin. Defaults to ['read','write']. Must be non-empty and within the scopes the caller's role permits. |
Request
Session-authenticated endpoint — sign in via the dashboard; a pm_live_ API key is rejected here.
curl -X POST https://pathmode.io/api/v1/api-keys \
-H "Cookie: sb-access-token=<your session>" \
-H "Content-Type: application/json" \
-d '{
"name": "CI pipeline key",
"workspaceId": "value"
}'Response
{
"id": "f1e2d3c4-b5a6-4978-8a90-1b2c3d4e5f60",
"key": "pm_live_3f2a9c1e8b4d4e2a9c1e7a6b5c4d3e2f0a1b2c3d4e5f60718293a4b5c6d7e8f9",
"name": "CI deploy key",
"keyPrefix": "pm_live_3f2a9c1e",
"scopes": ["read", "write"],
"createdAt": "2026-06-07T12:00:00.000Z",
"message": "Save this key now. It cannot be retrieved again."
}Cookie session only — never inspects Authorization header; pm_live_ Bearer NOT accepted. Caller must be admin or owner of workspaceId (workspace_members.role). Requested scopes are capped to deriveScopesFromRole(role); exceeding them is a 403. Billing-gated on the api_keys metric (403 with plan_limit_reached body). The secret 'key' (format pm_live_<64 hex chars>) is returned ONLY in this 201 response and can never be retrieved again — afterward only keyPrefix is exposed.
Status Codes
201 Key created; secret 'key' returned once.400 Missing name, missing workspaceId, empty/non-array scopes, or unrecognized scope values.401 No valid session cookie (API keys not accepted).403 Caller is not workspace admin/owner; requested scopes exceed role-derived scopes; OR billing limit reached (body { error:'plan_limit_reached', metric:'api_keys', current, limit, plan }).429 Rate limit exceeded (apiKeysWrite bucket, 20/h).500 Membership lookup failure, insert failure, or internal error.503 SUPABASE_SERVICE_ROLE_KEY not configured (admin client unavailable)./api/v1/api-keysRevoke (delete) one of the current user's API keys by id.
Request Body
| Name | Type | Description |
|---|---|---|
keyIdrequired | string (uuid) | Id of the api_keys row to delete; scoped to the authenticated user (eq user_id). |
Request
Session-authenticated endpoint — sign in via the dashboard; a pm_live_ API key is rejected here.
curl -X DELETE https://pathmode.io/api/v1/api-keys \
-H "Cookie: sb-access-token=<your session>" \
-H "Content-Type: application/json" \
-d '{
"keyId": "value"
}'Response
{
"success": true,
"message": "API key revoked"
}Cookie session only — never inspects Authorization header; pm_live_ Bearer NOT accepted. Delete filters on both id=keyId and user_id=current user, so a user can only revoke their own keys. No workspace role check (unlike POST). Returns a fixed { success:true, message } body regardless of whether a row actually matched.
Status Codes
200 Key revoked (delete is scoped to eq id + eq user_id; succeeds even if no row matched).400 Missing keyId.401 No valid session cookie (API keys not accepted).429 Rate limit exceeded (apiKeysWrite bucket, 20/h).500 Delete failure or internal error.503 SUPABASE_SERVICE_ROLE_KEY not configured (admin client unavailable).Pagination
List endpoints differ in how they page, and the count field means different things depending on the endpoint — read each below before relying on it.
GET /evidence
Paginated. Pass limit (default 50, max 200) and offset. The response echoes limit and offset, and count is the exact total across all pages. A next page exists while offset + limit < count.
GET /intents
Not paginated. Returns the full list for the workspace; count is the number of items returned (specs that fail validation are skipped, so count may be lower than the raw row count).
Rate Limits
Mutating and AI-heavy endpoints are rate-limited with a sliding window. Reads are not limited. When you exceed a limit you get 429 with a Retry-After header — back off and retry.
| Bucket | Limit | Window | Applies to |
|---|---|---|---|
| intents | 200 | 1 hour | POST /intents (intent creation), per user/IP. |
| general | 300 | 1 hour | POST /evidence, POST /intents/:id/evidence, POST /intents/:id/notes, per user/IP. |
| analyzeCodebase | 30 | 24 hours | POST /intents/:id/analyze-codebase, keyed per workspace. |
| apiKeysRead | 120 | 1 hour | GET /api-keys. |
| apiKeysWrite | 20 | 1 hour | POST and DELETE /api-keys. |
Response headers
| Header | Meaning |
|---|---|
| X-RateLimit-Limit | Maximum requests allowed in the current window. |
| X-RateLimit-Remaining | Requests remaining in the current window. |
| X-RateLimit-Reset | Unix timestamp (seconds) when the window resets. |
| Retry-After | Seconds to wait before retrying, sent on a 429 response. |
Errors & Status Codes
Every error response is JSON with at least an "error" string. Validation and quota errors include extra fields you should parse rather than assuming a flat envelope.
| Status | Meaning | Common cause |
|---|---|---|
200 | OK | Successful GET / PATCH / DELETE. |
201 | Created | Successful resource creation (POST /intents, /evidence, /notes, /api-keys). |
400 | Bad Request | Invalid body or query — validation errors are returned in details; or a session-authenticated call missing workspace_id. |
401 | Unauthorized | Missing or invalid API key, or no valid session cookie. |
403 | Forbidden | Insufficient scope, not a member of the workspace, or a plan limit was reached (see plan_limit_reached body). |
404 | Not Found | The intent, product, evidence, or key ID does not exist in this workspace. |
422 | Unprocessable | The intent is not ready for the requested action (e.g. an execute prompt for a spec with no real objective or outcome). |
429 | Too Many Requests | Rate limit exceeded. Honor the Retry-After header and back off. |
500 | Internal Error | Server-side issue — retry, then contact support if it persists. |
503 | Service Unavailable | An upstream AI provider is temporarily unavailable (verify, analyze). Retry with exponential backoff. |
Error response shapes
Authentication failure 401
{
"error": "Invalid API key"
}Validation error (Zod) 400
{
"error": "Invalid request body",
"details": {
"formErrors": [],
"fieldErrors": {
"objective": ["Required"],
"productId": ["Required"]
}
}
}Plan limit reached 403
{
"error": "plan_limit_reached",
"metric": "intents",
"current": 25,
"limit": 25,
"plan": "free"
}Versioning & Stability
The API is versioned in the path (/api/v1). Additive changes — new endpoints, new optional fields, new enum members — can ship within v1 without notice, so write tolerant clients that ignore unknown fields. Breaking changes ship under a new version path.
Endpoints marked Beta (codebase analysis) and the zero-config local MCP tools may change shape before they stabilize. AI-graded endpoints (verify, analyze) depend on an upstream model and can return 503.
Endpoints carry a Beta tag in the reference when their shape may still change. There are no webhooks today — poll the relevant GET endpoint (e.g. intents filtered by status) to observe changes. POSTs are not idempotent; guard retries in your own client.
MCP Tools
The @pathmode/mcp-server server exposes 22 tools, 5 prompts, and 3 resources to your AI coding agent. Required arguments are marked with *. See Getting Started to install it.
Read Tools
Query intents, evidence, workspace strategy, and the dependency graph. Read tools never modify data.
get_current_intent
Works without a keyGet the currently active intent, preferring approved intents with real objective/outcome content over empty stubs. Returns the full IntentSpec with objective, outcomes, constraints, and edge cases.
| Parameter | Type | Description |
|---|---|---|
| status | string | Filter by status: draft, validated, approved, shipped, verified |
list_intents
Works without a keyList all intents in the workspace. Returns an array of IntentSpecs with their status, objectives, and metadata.
| Parameter | Type | Description |
|---|---|---|
| status | string | Filter by status: draft, validated, approved, shipped, verified |
get_intent
Works without a keyGet a single intent by ID with full details including objective, outcomes, constraints, edge cases, and relations.
| Parameter | Type | Description |
|---|---|---|
| intentId* | string | The intent ID to fetch |
get_intent_relations
Requires API keyGet the dependency graph for a specific intent. Shows what it depends on, enables, or blocks.
| Parameter | Type | Description |
|---|---|---|
| intentId* | string | The intent ID to get relations for |
search_intents
Works without a keySearch intents by keyword. Searches across user goals, objectives, outcomes, and constraints.
| Parameter | Type | Description |
|---|---|---|
| query* | string | Search keyword or phrase |
| status | string | Filter by status: draft, validated, approved, shipped, verified |
analyze_intent_graph
Requires API keyAnalyze the intent dependency graph for risks and strategic insights. Returns critical path, cycles, bottlenecks, orphans, status mismatches, and stalled intents.
| Parameter | Type | Description |
|---|---|---|
| analysis | "full"|"critical-path"|"risks"|"status" | Type of analysis: full (default), critical-path, risks, or status distribution |
export_context
Requires API keyExport workspace context as a formatted file. Use "claude-md" for CLAUDE.md (full workspace context), "agents-md" for the same workspace context written for AGENTS.md (the native instruction file for OpenAI Codex and modern Cursor), "cursorrules" for Cursor AI rules, "intent-md" for a single intent specification file, or "outcome-rubric" for a Claude Managed Agents Outcomes rubric (writer description + evidence-forcing grader rubric) derived from the resolved intent, its implementation context, and the workspace constitution. claude-md and agents-md produce identical, agent-agnostic content (the difference is only the target filename). For cursorrules/intent-md/outcome-rubric, product context is always derived from the resolved intent. For claude-md/agents-md, pass productId to select a specific product, otherwise the first active product is used.
| Parameter | Type | Description |
|---|---|---|
| format* | "claude-md"|"agents-md"|"cursorrules"|"intent-md"|"outcome-rubric" | Export format |
| intentId | string | Intent ID (optional, for cursorrules and intent-md) |
| productId | string | Product ID (optional, only used for claude-md/agents-md format to select a specific product) |
get_agent_prompt
Requires API keyGet a formatted execution prompt for a specific intent. This is the full structured prompt including objective, outcomes, constraints, edge cases, and verification steps.
| Parameter | Type | Description |
|---|---|---|
| intentId* | string | The intent ID to generate a prompt for |
| mode | "draft"|"execute" | draft = critique the spec, execute = implement it |
get_workspace
Requires API keyGet workspace details including strategy (vision, non-negotiables, architecture principles), active products, and constitution rules.
No input parameters.
get_constitution
Requires API keyGet the workspace constitution rules. These are mandatory constraints that all implementations must respect.
No input parameters.
query_evidence
Requires API keySearch evidence items (friction points, user quotes, observations, metrics, feature requests) by product, type, severity, or text. Returns matching evidence with IDs that can be linked to intents.
| Parameter | Type | Description |
|---|---|---|
| productId | string | Filter by product (Space) ID |
| type | "friction"|"quote"|"observation"|"metric"|"request" | Filter by evidence type |
| severity | "low"|"medium"|"high"|"critical" | Filter by severity |
| search | string | Text search across evidence content |
| tags | string | Comma-separated tags to filter by |
| limit | number | Max results (default 50, max 200) |
Example: search for intents related to onboarding
// Tool call: search_intents
{
"query": "onboarding",
"status": "approved"
}Write Tools
Create and modify intents, evidence, and implementation notes. Write tools require an API key.
update_intent_status
Requires API keyUpdate the status of an intent. Use this to mark an intent as shipped after implementation, or verified after testing. For shipped/verified transitions, the response includes a verification checklist of outcomes, constitution rules, and health metrics that should be confirmed.
| Parameter | Type | Description |
|---|---|---|
| intentId* | string | The intent ID to update |
| status* | "draft"|"validated"|"approved"|"shipped"|"verified" | The new status |
log_implementation_note
Requires API keyRecord a technical decision or implementation note for an intent. Use this to document why you chose a specific approach.
| Parameter | Type | Description |
|---|---|---|
| intentId* | string | The intent ID to attach the note to |
| note* | string | The implementation note or technical decision |
record_implementation_finding
Requires API keyWrite a correction back to an intent when BUILDING it revealed the spec was wrong. Use this the moment you discover the spec assumed something the implementation contradicts — instead of only fixing it in your head or this chat, record it so the NEXT agent or person inherits the correction. This is the inverse of a decision: it closes the refinement loop so the same stale premise is not rebuilt. Open findings ride into future agent prompts until a human reconciles them into the spec.
| Parameter | Type | Description |
|---|---|---|
| intentId* | string | The intent ID whose spec the finding is about |
| assumption* | string | What the spec assumed or said before you built it |
| finding* | string | What building it actually revealed — the fact that contradicts the assumption |
| target | string | Which part of the spec this contradicts: "objective", "outcome:<id>", "constraint:<index>", "edgeCase:<id>", or plain prose |
| correction | string | Your proposed correction to the intent, if you have one |
| source | string | Where this came from, e.g. "claude-code @ owner/repo" — provenance for the audit trail |
create_intent
Requires API keyCreate a new intent spec in the workspace. Requires at minimum a title, objective, and productId. Returns the created intent with its ID. Use list_intents first to see existing intents and avoid duplicates.
| Parameter | Type | Description |
|---|---|---|
| title* | string | Short name for the intent (e.g., "Improve onboarding flow") |
| objective* | string | Why this matters — the problem and who has it |
| productId* | string | Product (Space) ID this intent belongs to. Use get_workspace to find product IDs. |
| outcomes | string[] | Observable, testable state changes |
| constraints | string[] | Hard limits the implementation must respect |
| healthMetrics | string[] | What to monitor after shipping |
| edgeCases | { scenario: string; expectedBehavior: string }[] | Failure modes and boundary conditions |
| verification | { manualChecks?: string[]; unitTests?: string[]; e2eTests?: string[] } | How to confirm it works |
| problemSeverity | "low"|"medium"|"high"|"critical" | How severe the problem is |
update_intent
Requires API keyUpdate an existing intent's content. Provide only the fields you want to change. Does NOT change intent status (use update_intent_status for that).
| Parameter | Type | Description |
|---|---|---|
| intentId* | string | The intent ID to update |
| title | string | New title |
| objective | string | Updated objective |
| outcomes | string[] | Replace all outcomes |
| constraints | string[] | Replace all constraints |
| healthMetrics | string[] | Replace all health metrics |
| edgeCases | { scenario: string; expectedBehavior: string }[] | Replace all edge cases |
| verification | { manualChecks?: string[]; unitTests?: string[]; e2eTests?: string[] } | Replace verification plan |
| problemSeverity | "low"|"medium"|"high"|"critical" |
create_evidence
Requires API keyCreate a new evidence item (e.g., a discovered bug, user feedback quote, behavioral observation, or feature request). Evidence can later be linked to intents to support prioritization.
| Parameter | Type | Description |
|---|---|---|
| content* | string | The evidence content — a finding, quote, or observation |
| type* | "friction"|"quote"|"observation"|"metric"|"request" | Type of evidence |
| productId* | string | Product (Space) ID this evidence belongs to |
| source | string | Where this evidence came from (e.g., "User interview", "Bug report", "Support ticket") |
| sourceUrl | string | URL to the original source |
| severity | "low"|"medium"|"high"|"critical" | Severity level (required for friction type) |
| sentiment | "positive"|"negative"|"neutral"|"mixed" | Emotional sentiment |
| tags | string[] | Category tags (e.g., ["Onboarding", "Performance"]) |
| stage | string | Workflow stage (e.g., "Discovery", "Checkout") |
link_evidence
Requires API keyLink or unlink evidence items to/from an intent. Linking evidence to intents establishes traceability between user problems and planned solutions.
| Parameter | Type | Description |
|---|---|---|
| intentId* | string | The intent ID to link evidence to |
| link | string[] | Evidence IDs to link to this intent |
| unlink | string[] | Evidence IDs to unlink from this intent |
verify_implementation
Requires API keyAI-grade your implementation against the intent spec. Checks each outcome, constraint, constitution rule, and edge case. Returns pass/fail per item with reasoning and an overall score. Also logs the result as an implementation note.
| Parameter | Type | Description |
|---|---|---|
| intentId* | string | The intent ID to verify against |
| summary* | string | Description of what was implemented and how |
| codeChanges | string | Summary of code changes (file list, key modifications) |
sync_context
Requires API keyWrite this workspace's canonical Pathmode context into the repo's CLAUDE.md or AGENTS.md, idempotently — the round-trip. Pulls the latest from Pathmode and replaces the PATHMODE-marked section in place so the repo's agent instructions never drift from the source of truth. Unlike export_context (which returns the text), this writes the file; re-running when nothing changed reports no drift.
| Parameter | Type | Description |
|---|---|---|
| format | "claude-md"|"agents-md" | Target file: claude-md → CLAUDE.md (default), agents-md → AGENTS.md |
| productId | string | Product ID (optional; defaults to the first active product) |
| path | string | Output file path relative to the project root. Defaults to CLAUDE.md / AGENTS.md |
Zero-Config Tools
These work without an API key or Pathmode account — they write intent specs straight to your repo as intent.md, .cursorrules, CLAUDE.md, or an outcome rubric.
intent_save
Works without a keySave an intent spec to intent.md in the project root. Called after building a spec through conversation.
| Parameter | Type | Description |
|---|---|---|
| spec* | IntentSpec | The intent spec object: { title: string; objective: string; outcomes: string[]; constraints?: string[]; edgeCases?: { scenario: string; expectedBehavior: string }[]; healthMetrics?: string[]; scope?: { inScope?: string[]; outOfScope?: string[] }; verification?: { manualChecks?: string[]; unitTests?: string[]; e2eTests?: string[] } } |
| path | string | File path relative to cwd. Defaults to intent.md |
intent_export
Works without a keyExport an intent spec as .cursorrules, a CLAUDE.md or AGENTS.md section, or a Claude Managed Agents Outcomes rubric for AI agent consumption. Use agents-md for Codex, Cursor, and other AGENTS.md-aware agents.
| Parameter | Type | Description |
|---|---|---|
| format* | "cursorrules"|"claude-md"|"agents-md"|"outcome-rubric" | Export format |
| spec* | IntentSpec | The intent spec object: { title: string; objective: string; outcomes: string[]; constraints?: string[]; edgeCases?: { scenario: string; expectedBehavior: string }[]; healthMetrics?: string[]; scope?: { inScope?: string[]; outOfScope?: string[] }; verification?: { manualChecks?: string[]; unitTests?: string[]; e2eTests?: string[] } } |
| path | string | Output file path. Defaults to .cursorrules, CLAUDE.md, or AGENTS.md |
Prompts
Pre-built prompt templates invoked through your MCP client's prompt interface (e.g. /implement-intent). Prompts with a * argument require it — invoking them bare will fail validation.
/implement-intent
1 argumentGet full implementation context for a specific intent, including objective, outcomes, constraints, edge cases, and verification steps.
| intentId* | The intent ID to implement |
/review-risks
No argumentsAnalyze the intent graph for architectural risks, circular dependencies, bottlenecks, and stalled work.
/what-next
No argumentsSuggest the highest-priority intent to work on next, based on dependency graph analysis and current status.
/create-from-evidence
1 argumentSearch for evidence items, identify patterns, and create an intent based on the strongest evidence cluster.
| productId* | The product ID to search evidence for |
/compile-intent
No argumentsStart a Socratic conversation to build a structured intent spec from user feedback or a problem description. No Pathmode account needed.
Example: build a spec through conversation (no arguments)
# In Claude Code, invoke the prompt directly:
/compile-intent
# Or implement a specific intent (intentId is required):
/implement-intent intentId=3f2a9c1e-8b4d-4e2a-9c1e-7a6b5c4d3e2fResources
MCP resources expose current workspace data, fetched on read. They are not subscribable — each read returns the latest snapshot; there are no live update notifications.
intent://current
Static read callback returning the currently active intent as JSON. In cloud mode returns the first approved intent (falling back to first intent of any status); in local mode returns the first locally-read intent.
intent://graph
Static read callback returning the intent dependency graph as JSON — an array of { id, title, status, relations } for every intent. Cloud mode only; returns an error in local mode.
intent://workspace-strategy
Static read callback returning workspace name, strategy, and active constitution rules as JSON. Cloud mode only; returns an error in local mode.
Data Models
TypeScript interfaces for the objects returned by the REST API and MCP tools. All IDs are UUIDs — they have no type prefix. Write tolerant clients: new optional fields can appear within v1.
ApiIntent
The core intent spec — one unit of planned work with its objective, outcomes, constraints, edge cases, and verification plan. Intents move through a lifecycle: draft → validated → approved → shipped → verified. The list endpoint returns lighter rows (edgeCases and evidenceIds come back empty); fetch the detail endpoint for the full object.
interface ApiIntent {
id: string; // UUID
workspaceId: string;
productId?: string;
stageName: string | null;
status: "draft" | "validated" | "approved" | "shipped" | "verified";
version: number;
title: string;
objective: string;
problemSeverity: "low" | "medium" | "high" | "critical" | null;
outcomes: { id: string; text: string; priority?: "must" | "should" | "could" }[];
healthMetrics: string[];
scope: { inScope?: string[]; outOfScope?: string[] } | null;
strategicAlignment: string | null;
alignmentNotes: string | null;
context: Record<string, unknown>;
constraints: string[];
verification: { manualChecks?: string[]; unitTests?: string[]; e2eTests?: string[] };
externalLinks: unknown[];
evidenceAnchors: Record<string, string[]>;
implementationContext: ImplementationContext | null;
evidenceIds: string[]; // empty on the list endpoint; populated on GET /intents/:id
edgeCases: { // empty on the list endpoint; populated on GET /intents/:id
id: string;
scenario: string;
expectedBehavior: string;
}[];
relations: { targetId: string; type: string }[];
createdAt: string;
updatedAt: string;
shippedAt: string | null;
verifiedAt: string | null;
}ImplementationContext
Produced by the codebase-analysis endpoint and stored on the intent. Summarizes the relevant areas of the bound repo, current behavior, lift estimate, and risks for an AI agent about to implement the spec.
interface ImplementationContext {
relevantAreas: { path: string; reason: string }[];
currentBehavior: string;
liftEstimate: "low" | "medium" | "high";
risks: string[];
verificationSuggestions: string[];
analyzedAt: string;
analyzedRepo: { provider: "github"; owner: string; name: string; ref: string };
analyzedSpecVersion: string;
}ApiEvidence
An evidence item — a friction point, user quote, observation, metric, or feature request. Linked to a product, and anchorable to specific parts of an intent spec.
interface ApiEvidence {
id: string;
productId: string;
workspaceId: string;
type: "friction" | "quote" | "observation" | "metric" | "request";
content: string;
source?: string;
sourceUrl?: string;
severity?: "low" | "medium" | "high" | "critical";
sentiment?: "positive" | "negative" | "neutral" | "mixed";
tags: string[];
stage?: string;
linkedIntentIds: string[];
createdAt: string;
updatedAt: string;
}ApiWorkspace
The top-level workspace: strategy (vision, trade-offs, architecture principles), the products in it, and the constitution rules every implementation must respect.
interface ApiWorkspace {
id: string;
name: string;
urlKey?: string;
tags: string[];
strategy: {
vision?: string;
tradeoffs?: string[];
nonNegotiables?: string[];
architecturePrinciples?: string[];
} | null;
products: {
id: string;
workspaceId: string;
name: string;
slug: string;
description?: string;
defaultContext?: string;
productVision?: string;
northStar?: string;
targetAudience?: string;
manifest?: { constraints?: string[]; principles?: string[]; keyDecisions?: string[] };
icon?: string;
color?: string;
status: string;
createdBy?: string;
createdAt: string;
updatedAt: string;
}[];
constitutionRules: ConstitutionRule[];
}
interface ConstitutionRule {
id: string;
category: string;
text: string;
rationale?: string;
scope?: string;
enforcement: string; // defaults to "required"
examples?: string[];
isActive: boolean;
createdAt: string;
}ApiVerificationResult
Returned by verify. A pass/fail verdict, an overall 0-100 score, per-item results with reasoning, and a human-readable summary.
interface ApiVerificationResult {
pass: boolean;
score: number; // 0-100
results: {
item: string;
category: string; // outcome | constraint | constitution | edge_case
status: string; // pass | fail
reasoning: string;
}[];
summary: string;
}Key field reference
| Field | Model | Notes |
|---|---|---|
| evidenceAnchors | ApiIntent | Maps spec sections to evidence IDs. Keys: "objective", "outcome:0", "edgeCase:<uuid>", "constraint:0". |
| relations | ApiIntent | Dependency links. Types include depends_on, enables, blocks. |
| enforcement | ConstitutionRule | How strictly the rule is applied during verification. Defaults to required; only active rules are enforced. |
| status | ApiIntent | Lifecycle: draft → validated → approved → shipped → verified. Shipped/verified transitions return a verification checklist. |
| score | ApiVerificationResult | 0-100, based on how many outcomes, constraints, and constitution rules the implementation satisfies. |
Use Cases
Common workflows that show how the MCP tools fit together in practice.
1. CI Verification
Use verify_implementation after deployment to check whether your code actually satisfies the intent spec. This closes the loop between what you planned and what you shipped — catching drift before it reaches users.
Step 1: Fetch the intent you are implementing
// Step 1: Fetch the intent spec your code implements
// Tool call: get_intent
{
"intentId": "3f2a9c1e-8b4d-4e2a-9c1e-7a6b5c4d3e2f"
}Step 2: Verify your implementation against the spec
// Step 2: After implementing, verify against the spec
// Tool call: verify_implementation
{
"intentId": "3f2a9c1e-8b4d-4e2a-9c1e-7a6b5c4d3e2f",
"summary": "Added inline validation to the payment form. Error messages now appear next to the invalid field within 100ms. Payment form lazy-loads and renders in under 400ms on 3G.",
"codeChanges": "Modified: components/PaymentForm.tsx, lib/validation.ts, tests/payment.spec.ts"
}Step 3: Mark as shipped if verification passes
// Step 3: If verification passes, mark as shipped
// Tool call: update_intent_status
{
"intentId": "3f2a9c1e-8b4d-4e2a-9c1e-7a6b5c4d3e2f",
"status": "shipped"
}Step 4: Log implementation decisions
// Step 4: Log any implementation decisions for the record
// Tool call: log_implementation_note
{
"intentId": "3f2a9c1e-8b4d-4e2a-9c1e-7a6b5c4d3e2f",
"note": "Used Zod for inline validation instead of HTML5 constraint API — better error messages and consistent behavior across browsers."
}Tip: In a CI pipeline, you can fail the build if verify_implementation returns pass: falseor a score below your threshold. This prevents shipping code that doesn't match the agreed spec.
2. Evidence-Driven Specs
Start from real user problems instead of assumptions. Collect evidence first — friction points, user quotes, metrics — then create intents grounded in what you have observed. Linking evidence to intents creates traceability from user pain to shipped solution.
Step 1: Record evidence from user research
// Step 1: Record evidence from user research
// Tool call: create_evidence
{
"content": "3 out of 5 users couldn't find the settings page. They expected it in the top nav, not buried in the profile dropdown.",
"type": "friction",
"productId": "7d8e9f0a-1b2c-4d3e-9f8a-6b5c4d3e2f1a",
"source": "Usability test — March 2026",
"severity": "high",
"tags": ["navigation", "settings"],
"stage": "Activation"
}Step 2: Query for patterns across evidence
// Step 2: Query for patterns across evidence
// Tool call: query_evidence
{
"productId": "7d8e9f0a-1b2c-4d3e-9f8a-6b5c4d3e2f1a",
"type": "friction",
"severity": "high",
"search": "navigation"
}Step 3: Create an intent informed by the evidence
// Step 3: Create an intent informed by the evidence
// Tool call: create_intent
{
"title": "Surface settings in primary navigation",
"objective": "Users cannot find settings because the link is hidden in the profile dropdown. 3 of 5 usability test participants failed this task.",
"productId": "7d8e9f0a-1b2c-4d3e-9f8a-6b5c4d3e2f1a",
"outcomes": [
"Settings link appears in the top navigation bar on all viewport sizes",
"Users can reach settings in one click from any page",
"Usability test completion rate for 'change notification preferences' exceeds 90%"
],
"constraints": [
"Top nav must not exceed 5 primary items",
"Mobile nav uses the same information architecture as desktop"
],
"problemSeverity": "high"
}Step 4: Link the evidence to the new intent
// Step 4: Link the evidence to the new intent
// Tool call: link_evidence
{
"intentId": "5b6c7d8e-9f0a-4b1c-8d2e-3f4a5b6c7d8e",
"link": ["a1b2c3d4-0001-4a7b-8c9d-0e1f2a3b4c5d", "a1b2c3d4-0002-4a7b-8c9d-0e1f2a3b4c5d", "a1b2c3d4-0003-4a7b-8c9d-0e1f2a3b4c5d"]
}Tip: Use the create-from-evidence prompt to automate this workflow. It searches your evidence, identifies patterns, and drafts an intent spec with evidence already linked.
3. Agent Context Injection
Feed your workspace strategy and intent specs into AI coding agents so they understand what to build and why. This ensures every AI-generated line of code is aligned with your product intent — not just technically correct, but solving the right problem.
Option A: Full workspace context as CLAUDE.md
// Option A: Export full workspace context as CLAUDE.md
// Tool call: export_context
{
"format": "claude-md"
}
// This generates a CLAUDE.md section with:
// - Workspace strategy and vision
// - Constitution rules
// - Active intents and their statuses
// - Product contextOption B: Focused execution prompt for one intent
// Option B: Get a focused execution prompt for one intent
// Tool call: get_agent_prompt
{
"intentId": "3f2a9c1e-8b4d-4e2a-9c1e-7a6b5c4d3e2f",
"mode": "execute"
}
// Returns a structured prompt containing:
// - Objective and why it matters
// - Outcomes to achieve (testable criteria)
// - Constraints to respect
// - Edge cases to handle
// - Verification steps
// - Linked evidence for contextOption C: Export as .cursorrules for Cursor AI
// Option C: Export as .cursorrules for Cursor AI
// Tool call: export_context
{
"format": "cursorrules",
"intentId": "3f2a9c1e-8b4d-4e2a-9c1e-7a6b5c4d3e2f"
}
// Writes a .cursorrules file that Cursor reads automatically,
// injecting intent context into every AI interaction.Option D: Local-only export (no API key needed)
// Option D: Use intent_export for local-only workflows
// Tool call: intent_export
{
"format": "claude-md",
"spec": {
"title": "Improve checkout conversion",
"objective": "Users abandon cart at payment step...",
"outcomes": [
"Payment form loads in under 500ms",
"Error messages appear inline next to the field"
],
"constraints": [
"No third-party payment scripts on initial load"
]
}
}
// Works without an API key — writes directly to your repo.Tip: Combine get_agent_prompt with get_constitution to give your agent both the specific task and the global rules it must respect. Constitution rules act as guardrails that apply across all intents.