{
  "openapi": "3.1.0",
  "info": {
    "title": "AskCV Agent Capability API",
    "version": "1.0.0",
    "description": "The external agent surface: every MCP capability, invocable over plain HTTP at `POST /api/v1/capabilities/{name}`. This document is GENERATED from the live tool registry (`bun run gen:openapi`) — the `input` schemas are the exact bytes the server serves on `GET /api/v1/capabilities` and `/api/mcp/manifest`.\n\nThe hand-rolled `/api/agent/*` endpoints it replaced are ALL RETIRED as of 2026-08-04. Every one returns `410 Gone` naming its replacement — goals, depth, coverage, evidence, profile, health, import, batch-status, both exports, query and fit-check — with no authentication and no data access behind them. `/api/agent/health`'s discovery role moved here, and to `/api/health` for liveness.\n\nThere is no legacy endpoint left to fall back to: build against the capabilities below. Migration is scope-for-scope — each successor accepts the same `agent:read` / `agent:write` / `agent:query` token the endpoint it replaces required. Two deltas are worth knowing: `profile.get` returns a narrower digest than `/api/agent/profile` did and needs an explicit `tool:profile.get` grant, and the export capabilities do not carry the routes' `?format=jsonld` rendering."
  },
  "servers": [
    {
      "url": "https://askcv.ai"
    }
  ],
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "tags": [
    {
      "name": "discovery",
      "description": "Capability catalog."
    },
    {
      "name": "reads",
      "description": "Read capabilities — no side effects."
    },
    {
      "name": "writes",
      "description": "Write capabilities. Require an `Idempotency-Key` header; a coarse-granted write stages for owner approval instead of executing inline."
    }
  ],
  "paths": {
    "/api/v1/capabilities": {
      "get": {
        "operationId": "listCapabilities",
        "summary": "List the capabilities this token can reach",
        "description": "Returns only the capabilities the caller's token actually authorizes (discovery matches execution). A non-free read is additionally omitted from this listing because the route has no tenant billing context to filter against, even though an explicitly-granted token can call it — those capabilities stay documented here with `x-askcv-min-tier`.",
        "tags": [
          "discovery"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "The authorized capability catalog.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "capabilities": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/CapabilityListEntry"
                      }
                    }
                  },
                  "required": [
                    "capabilities"
                  ]
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid bearer token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "The token holds none of `agent:read` / `agent:write` / `agent:query`, or is not an `agent`-type credential.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded. Body: `{ error, retryAfterMs }`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/capabilities/cadence.answerAsk": {
      "post": {
        "operationId": "capability_cadence_answerAsk",
        "summary": "cadence.answerAsk",
        "description": "Answer the pending weekly check-in ask (`cadence.getPendingAsk`). Routes through the same persistence the Sunday weekly-win email reply uses, so answering here closes the email-side ask too. A no-op (`status: \"duplicate\"`) if the week was already answered. GRANT PROVENANCE decides the outcome: reached via an explicit `tool:<name>` grant on a token that does NOT also hold `agent:write`, this executes inline and returns 200; reached via the coarse `agent:write` grant (including a token holding both), it is staged for owner approval and returns 202.",
        "tags": [
          "writes"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "input": {
                    "$schema": "https://json-schema.org/draft/2020-12/schema",
                    "type": "object",
                    "properties": {
                      "answer": {
                        "type": "string",
                        "minLength": 1,
                        "maxLength": 2000,
                        "description": "The user's answer to the pending weekly check-in ask (e.g. this week's weekly-win prompt), in their own words."
                      },
                      "weekISO": {
                        "type": "string",
                        "minLength": 1,
                        "description": "The ISO week label the ask was staged under — the `weekISO` field returned by `cadence.getPendingAsk`. Answer the SAME week you were asked about."
                      },
                      "askDigest": {
                        "type": "string",
                        "minLength": 1,
                        "description": "The `askDigest` field from the `cadence.getPendingAsk` call that surfaced this ask. Binds your answer to the EXACT question text you were shown — if the pending ask changed since (e.g. an internal pin landed with a different question), this call is rejected rather than silently saving your answer under a question the user never saw. Call cadence.getPendingAsk again to get the current digest."
                      }
                    },
                    "required": [
                      "answer",
                      "weekISO",
                      "askDigest"
                    ],
                    "additionalProperties": false
                  }
                },
                "required": [
                  "input"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Executed. The body is the capability's own result envelope plus `requestId`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "202": {
            "description": "Staged for owner approval (the grant was coarse, not an explicit per-capability grant). Body: `{ status: \"staged\", approvalId, requestId }`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "`VALIDATION_ERROR` (input failed the capability's schema), `IDEMPOTENCY_KEY_REQUIRED`, or `INVALID_JSON` (unparseable request body).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, expired, or revoked bearer token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "`SCOPE_DENIED` — the token lacks a required scope, or the tenant is below `x-askcv-min-tier`. Emitted by the route with the standard `ErrorEnvelope`; the shared agent-auth ENTRY gate emits the leaner `AuthErrorEnvelope` for a wrong-credential-type token, so both shapes are documented.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/ErrorEnvelope"
                    },
                    {
                      "$ref": "#/components/schemas/AuthErrorEnvelope"
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "description": "`NOT_FOUND` (unknown or non-exported capability) or `FEATURE_UNAVAILABLE` (currently launch-hidden).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "`STAGING_CONFLICT` — an identical staged action already exists. `retryable` is honest about whether retrying could change anything: `false` when the prior action was already DECIDED (the content-derived key means an unchanged retry lands on the same terminal row), `true` when it is still in flight.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "422": {
            "description": "`TOOL_FAILED` — the capability ran and reported a failure.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded — either the shared per-tenant surface budget or, when this operation declares `x-askcv-rate-limit`, its own tighter per-capability budget (charged on the same key over every transport). The budget really was spent, so `retryAfterMs` is an honest hint. Body: `{ error, retryAfterMs }`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        },
        "x-askcv-side": "write",
        "x-askcv-min-tier": "free",
        "x-askcv-cost-class": "cheap",
        "x-askcv-required-scopes": [
          "tool:cadence.answerAsk"
        ],
        "x-askcv-coarse-scopes": [
          [
            "agent:write"
          ]
        ],
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Required intent marker. Its VALUE is never looked up: dedupe is content-derived from the validated input, so an identical payload dedupes regardless of this header, and a reused key with a changed payload executes as a new write."
          }
        ]
      }
    },
    "/api/v1/capabilities/cadence.getPendingAsk": {
      "post": {
        "operationId": "capability_cadence_getPendingAsk",
        "summary": "cadence.getPendingAsk",
        "description": "Check whether the user has a pending weekly check-in ask (e.g. this week's weekly-win prompt). Returns `ask: null` when there is nothing pending — either because they already answered this week (by email or a prior `cadence.answerAsk` call) or none is due.",
        "tags": [
          "reads"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "input": {
                    "$schema": "https://json-schema.org/draft/2020-12/schema",
                    "type": "object",
                    "properties": {},
                    "additionalProperties": false
                  }
                },
                "required": [
                  "input"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The capability's own result envelope plus `requestId`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "`VALIDATION_ERROR` (input failed the capability's schema) or `INVALID_JSON` (unparseable request body).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, expired, or revoked bearer token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "`SCOPE_DENIED` — the token lacks a required scope, or the tenant is below `x-askcv-min-tier`. Emitted by the route with the standard `ErrorEnvelope`; the shared agent-auth ENTRY gate emits the leaner `AuthErrorEnvelope` for a wrong-credential-type token, so both shapes are documented.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/ErrorEnvelope"
                    },
                    {
                      "$ref": "#/components/schemas/AuthErrorEnvelope"
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "description": "`NOT_FOUND` (unknown or non-exported capability) or `FEATURE_UNAVAILABLE` (currently launch-hidden).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "422": {
            "description": "`TOOL_FAILED` — the capability ran and reported a failure.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded — either the shared per-tenant surface budget or, when this operation declares `x-askcv-rate-limit`, its own tighter per-capability budget (charged on the same key over every transport). The budget really was spent, so `retryAfterMs` is an honest hint. Body: `{ error, retryAfterMs }`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        },
        "x-askcv-side": "read",
        "x-askcv-min-tier": "free",
        "x-askcv-cost-class": "cheap",
        "x-askcv-required-scopes": [
          "tool:cadence.getPendingAsk"
        ],
        "x-askcv-coarse-scopes": [
          [
            "agent:query"
          ],
          [
            "agent:read"
          ]
        ]
      }
    },
    "/api/v1/capabilities/career.ask": {
      "post": {
        "operationId": "capability_career_ask",
        "summary": "career.ask",
        "description": "Ask a career question about the represented person and get an answer grounded in their published career knowledge, with a count of the sources used. Requires agent:query scope and a Plus subscription; rate limited to 30 questions per hour.",
        "tags": [
          "reads"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "input": {
                    "$schema": "https://json-schema.org/draft/2020-12/schema",
                    "type": "object",
                    "properties": {
                      "question": {
                        "type": "string",
                        "minLength": 1,
                        "maxLength": 2000,
                        "description": "The career question to answer about the represented person, e.g. 'What is their experience leading distributed teams?'. Answered only from their published career knowledge."
                      }
                    },
                    "required": [
                      "question"
                    ],
                    "additionalProperties": false
                  }
                },
                "required": [
                  "input"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The capability's own result envelope plus `requestId`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "`VALIDATION_ERROR` (input failed the capability's schema) or `INVALID_JSON` (unparseable request body).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, expired, or revoked bearer token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "`SCOPE_DENIED` — the token lacks a required scope, or the tenant is below `x-askcv-min-tier`. Emitted by the route with the standard `ErrorEnvelope`; the shared agent-auth ENTRY gate emits the leaner `AuthErrorEnvelope` for a wrong-credential-type token, so both shapes are documented.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/ErrorEnvelope"
                    },
                    {
                      "$ref": "#/components/schemas/AuthErrorEnvelope"
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "description": "`NOT_FOUND` (unknown or non-exported capability) or `FEATURE_UNAVAILABLE` (currently launch-hidden).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "422": {
            "description": "`TOOL_FAILED` — the capability ran and reported a failure.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded — either the shared per-tenant surface budget or, when this operation declares `x-askcv-rate-limit`, its own tighter per-capability budget (charged on the same key over every transport). The budget really was spent, so `retryAfterMs` is an honest hint. Body: `{ error, retryAfterMs }`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "503": {
            "description": "`RATE_LIMITER_UNAVAILABLE` — rate limiting is temporarily unavailable and the request was refused defensively (a cost-bearing check fails CLOSED rather than letting an expensive call through unmetered). Distinct from 429: NO budget was consumed, so this is NOT a signal to back off against your quota. `retryable` is `true` and retrying shortly can succeed. Deliberately carries no `retryAfterMs`, because the limiter reports a full window on this path and quoting it would be a false retry hint.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "x-askcv-side": "read",
        "x-askcv-min-tier": "plus",
        "x-askcv-cost-class": "expensive",
        "x-askcv-required-scopes": [
          "tool:career.ask",
          "agent:query"
        ],
        "x-askcv-coarse-scopes": [],
        "x-askcv-rate-limit": {
          "perHour": 30,
          "scope": "tenant",
          "sharedAcrossTransports": true
        }
      }
    },
    "/api/v1/capabilities/coaching.addActionItem": {
      "post": {
        "operationId": "capability_coaching_addActionItem",
        "summary": "coaching.addActionItem",
        "description": "Record a concrete action item the user has committed to. Optionally linked to a coaching session. Reversible — undo deletes the action item row. GRANT PROVENANCE decides the outcome: reached via an explicit `tool:<name>` grant on a token that does NOT also hold `agent:write`, this executes inline and returns 200; reached via the coarse `agent:write` grant (including a token holding both), it is staged for owner approval and returns 202.",
        "tags": [
          "writes"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "input": {
                    "$schema": "https://json-schema.org/draft/2020-12/schema",
                    "type": "object",
                    "properties": {
                      "title": {
                        "type": "string",
                        "minLength": 1,
                        "maxLength": 200,
                        "description": "Specific, short action description."
                      },
                      "description": {
                        "anyOf": [
                          {
                            "type": "string",
                            "maxLength": 2000
                          },
                          {
                            "type": "null"
                          }
                        ]
                      },
                      "category": {
                        "default": "follow_up",
                        "type": "string",
                        "enum": [
                          "research",
                          "application",
                          "networking",
                          "skill_building",
                          "follow_up",
                          "reflection"
                        ]
                      },
                      "priority": {
                        "default": "medium",
                        "type": "string",
                        "enum": [
                          "high",
                          "medium",
                          "low"
                        ]
                      },
                      "dueDate": {
                        "anyOf": [
                          {
                            "type": "string",
                            "format": "date-time",
                            "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
                          },
                          {
                            "type": "null"
                          }
                        ]
                      },
                      "sessionId": {
                        "anyOf": [
                          {
                            "type": "string",
                            "format": "uuid",
                            "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
                          },
                          {
                            "type": "null"
                          }
                        ]
                      }
                    },
                    "required": [
                      "title",
                      "category",
                      "priority"
                    ],
                    "additionalProperties": false
                  }
                },
                "required": [
                  "input"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Executed. The body is the capability's own result envelope plus `requestId`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "202": {
            "description": "Staged for owner approval (the grant was coarse, not an explicit per-capability grant). Body: `{ status: \"staged\", approvalId, requestId }`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "`VALIDATION_ERROR` (input failed the capability's schema), `IDEMPOTENCY_KEY_REQUIRED`, or `INVALID_JSON` (unparseable request body).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, expired, or revoked bearer token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "`SCOPE_DENIED` — the token lacks a required scope, or the tenant is below `x-askcv-min-tier`. Emitted by the route with the standard `ErrorEnvelope`; the shared agent-auth ENTRY gate emits the leaner `AuthErrorEnvelope` for a wrong-credential-type token, so both shapes are documented.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/ErrorEnvelope"
                    },
                    {
                      "$ref": "#/components/schemas/AuthErrorEnvelope"
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "description": "`NOT_FOUND` (unknown or non-exported capability) or `FEATURE_UNAVAILABLE` (currently launch-hidden).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "`STAGING_CONFLICT` — an identical staged action already exists. `retryable` is honest about whether retrying could change anything: `false` when the prior action was already DECIDED (the content-derived key means an unchanged retry lands on the same terminal row), `true` when it is still in flight.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "422": {
            "description": "`TOOL_FAILED` — the capability ran and reported a failure.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded — either the shared per-tenant surface budget or, when this operation declares `x-askcv-rate-limit`, its own tighter per-capability budget (charged on the same key over every transport). The budget really was spent, so `retryAfterMs` is an honest hint. Body: `{ error, retryAfterMs }`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        },
        "x-askcv-side": "write",
        "x-askcv-min-tier": "free",
        "x-askcv-cost-class": "cheap",
        "x-askcv-required-scopes": [
          "tool:coaching.addActionItem"
        ],
        "x-askcv-coarse-scopes": [
          [
            "agent:write"
          ]
        ],
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Required intent marker. Its VALUE is never looked up: dedupe is content-derived from the validated input, so an identical payload dedupes regardless of this header, and a reused key with a changed payload executes as a new write."
          }
        ]
      }
    },
    "/api/v1/capabilities/coaching.listActionItems": {
      "post": {
        "operationId": "capability_coaching_listActionItems",
        "summary": "coaching.listActionItems",
        "description": "List coaching action items — open commitments (pending / in_progress / deferred) or completed items. Paginated. Use when following up on things the user said they'd do.",
        "tags": [
          "reads"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "input": {
                    "$schema": "https://json-schema.org/draft/2020-12/schema",
                    "type": "object",
                    "properties": {
                      "status": {
                        "description": "`open` = pending + in_progress + deferred; `done` = completed. Omit for all.",
                        "type": "string",
                        "enum": [
                          "open",
                          "done"
                        ]
                      },
                      "cursor": {
                        "description": "Opaque pagination cursor from a prior response. Omit for the first page.",
                        "anyOf": [
                          {
                            "type": "string"
                          },
                          {
                            "type": "null"
                          }
                        ]
                      },
                      "limit": {
                        "default": 20,
                        "description": "Maximum items to return in this page (1-50). Default 20.",
                        "type": "integer",
                        "minimum": 1,
                        "maximum": 50
                      }
                    },
                    "required": [
                      "limit"
                    ],
                    "additionalProperties": false
                  }
                },
                "required": [
                  "input"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The capability's own result envelope plus `requestId`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "`VALIDATION_ERROR` (input failed the capability's schema) or `INVALID_JSON` (unparseable request body).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, expired, or revoked bearer token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "`SCOPE_DENIED` — the token lacks a required scope, or the tenant is below `x-askcv-min-tier`. Emitted by the route with the standard `ErrorEnvelope`; the shared agent-auth ENTRY gate emits the leaner `AuthErrorEnvelope` for a wrong-credential-type token, so both shapes are documented.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/ErrorEnvelope"
                    },
                    {
                      "$ref": "#/components/schemas/AuthErrorEnvelope"
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "description": "`NOT_FOUND` (unknown or non-exported capability) or `FEATURE_UNAVAILABLE` (currently launch-hidden).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "422": {
            "description": "`TOOL_FAILED` — the capability ran and reported a failure.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded — either the shared per-tenant surface budget or, when this operation declares `x-askcv-rate-limit`, its own tighter per-capability budget (charged on the same key over every transport). The budget really was spent, so `retryAfterMs` is an honest hint. Body: `{ error, retryAfterMs }`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        },
        "x-askcv-side": "read",
        "x-askcv-min-tier": "free",
        "x-askcv-cost-class": "cheap",
        "x-askcv-required-scopes": [
          "tool:coaching.listActionItems"
        ],
        "x-askcv-coarse-scopes": []
      }
    },
    "/api/v1/capabilities/coaching.logInsight": {
      "post": {
        "operationId": "capability_coaching_logInsight",
        "summary": "coaching.logInsight",
        "description": "Log a meaningful coaching moment (insight, belief shift, goal clarified, commitment made) against a session. Reversible — undo deletes the milestone row. GRANT PROVENANCE decides the outcome: reached via an explicit `tool:<name>` grant on a token that does NOT also hold `agent:write`, this executes inline and returns 200; reached via the coarse `agent:write` grant (including a token holding both), it is staged for owner approval and returns 202.",
        "tags": [
          "writes"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "input": {
                    "$schema": "https://json-schema.org/draft/2020-12/schema",
                    "type": "object",
                    "properties": {
                      "sessionId": {
                        "type": "string",
                        "format": "uuid",
                        "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
                        "description": "Coaching session this insight belongs to."
                      },
                      "label": {
                        "type": "string",
                        "minLength": 1,
                        "maxLength": 80,
                        "description": "Short label (2–6 words). Examples: 'Key insight surfaced', 'Limiting belief identified'."
                      },
                      "summary": {
                        "type": "string",
                        "minLength": 1,
                        "maxLength": 500,
                        "description": "1–2 sentence description of the moment."
                      }
                    },
                    "required": [
                      "sessionId",
                      "label",
                      "summary"
                    ],
                    "additionalProperties": false
                  }
                },
                "required": [
                  "input"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Executed. The body is the capability's own result envelope plus `requestId`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "202": {
            "description": "Staged for owner approval (the grant was coarse, not an explicit per-capability grant). Body: `{ status: \"staged\", approvalId, requestId }`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "`VALIDATION_ERROR` (input failed the capability's schema), `IDEMPOTENCY_KEY_REQUIRED`, or `INVALID_JSON` (unparseable request body).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, expired, or revoked bearer token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "`SCOPE_DENIED` — the token lacks a required scope, or the tenant is below `x-askcv-min-tier`. Emitted by the route with the standard `ErrorEnvelope`; the shared agent-auth ENTRY gate emits the leaner `AuthErrorEnvelope` for a wrong-credential-type token, so both shapes are documented.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/ErrorEnvelope"
                    },
                    {
                      "$ref": "#/components/schemas/AuthErrorEnvelope"
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "description": "`NOT_FOUND` (unknown or non-exported capability) or `FEATURE_UNAVAILABLE` (currently launch-hidden).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "`STAGING_CONFLICT` — an identical staged action already exists. `retryable` is honest about whether retrying could change anything: `false` when the prior action was already DECIDED (the content-derived key means an unchanged retry lands on the same terminal row), `true` when it is still in flight.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "422": {
            "description": "`TOOL_FAILED` — the capability ran and reported a failure.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded — either the shared per-tenant surface budget or, when this operation declares `x-askcv-rate-limit`, its own tighter per-capability budget (charged on the same key over every transport). The budget really was spent, so `retryAfterMs` is an honest hint. Body: `{ error, retryAfterMs }`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        },
        "x-askcv-side": "write",
        "x-askcv-min-tier": "plus",
        "x-askcv-cost-class": "cheap",
        "x-askcv-required-scopes": [
          "tool:coaching.logInsight"
        ],
        "x-askcv-coarse-scopes": [
          [
            "agent:write"
          ]
        ],
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Required intent marker. Its VALUE is never looked up: dedupe is content-derived from the validated input, so an identical payload dedupes regardless of this header, and a reused key with a changed payload executes as a new write."
          }
        ]
      }
    },
    "/api/v1/capabilities/conversations.listRecent": {
      "post": {
        "operationId": "capability_conversations_listRecent",
        "summary": "conversations.listRecent",
        "description": "List the user's recent conversations across two surfaces, newest first: coaching sessions and public-chat sessions (visitors chatting with the user's public agent). Coaching rows are metadata only (title, when it started, message count) — never message text. Public-chat rows may include a brief excerpt of the most recent exchange. Paginated.",
        "tags": [
          "reads"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "input": {
                    "$schema": "https://json-schema.org/draft/2020-12/schema",
                    "type": "object",
                    "properties": {
                      "cursor": {
                        "description": "Opaque pagination cursor from a prior response. Omit for the first page.",
                        "anyOf": [
                          {
                            "type": "string"
                          },
                          {
                            "type": "null"
                          }
                        ]
                      },
                      "limit": {
                        "default": 20,
                        "description": "Maximum items to return in this page (1-50). Default 20.",
                        "type": "integer",
                        "minimum": 1,
                        "maximum": 50
                      }
                    },
                    "required": [
                      "limit"
                    ],
                    "additionalProperties": false
                  }
                },
                "required": [
                  "input"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The capability's own result envelope plus `requestId`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "`VALIDATION_ERROR` (input failed the capability's schema) or `INVALID_JSON` (unparseable request body).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, expired, or revoked bearer token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "`SCOPE_DENIED` — the token lacks a required scope, or the tenant is below `x-askcv-min-tier`. Emitted by the route with the standard `ErrorEnvelope`; the shared agent-auth ENTRY gate emits the leaner `AuthErrorEnvelope` for a wrong-credential-type token, so both shapes are documented.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/ErrorEnvelope"
                    },
                    {
                      "$ref": "#/components/schemas/AuthErrorEnvelope"
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "description": "`NOT_FOUND` (unknown or non-exported capability) or `FEATURE_UNAVAILABLE` (currently launch-hidden).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "422": {
            "description": "`TOOL_FAILED` — the capability ran and reported a failure.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded — either the shared per-tenant surface budget or, when this operation declares `x-askcv-rate-limit`, its own tighter per-capability budget (charged on the same key over every transport). The budget really was spent, so `retryAfterMs` is an honest hint. Body: `{ error, retryAfterMs }`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        },
        "x-askcv-side": "read",
        "x-askcv-min-tier": "free",
        "x-askcv-cost-class": "cheap",
        "x-askcv-required-scopes": [
          "tool:conversations.listRecent"
        ],
        "x-askcv-coarse-scopes": []
      }
    },
    "/api/v1/capabilities/coverage.getGaps": {
      "post": {
        "operationId": "capability_coverage_getGaps",
        "summary": "coverage.getGaps",
        "description": "Get the user's coverage gaps for one end-use (review, promotion, or application) — a deterministic, ordered list of what's missing from their captured career evidence for that purpose (e.g. no quantified metrics, no recent evidence, too few interview stories). No AI call — purely computed from structured evidence fields.",
        "tags": [
          "reads"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "input": {
                    "$schema": "https://json-schema.org/draft/2020-12/schema",
                    "type": "object",
                    "properties": {
                      "endUse": {
                        "type": "string",
                        "enum": [
                          "review",
                          "promotion",
                          "application"
                        ]
                      }
                    },
                    "required": [
                      "endUse"
                    ],
                    "additionalProperties": false
                  }
                },
                "required": [
                  "input"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The capability's own result envelope plus `requestId`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "`VALIDATION_ERROR` (input failed the capability's schema) or `INVALID_JSON` (unparseable request body).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, expired, or revoked bearer token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "`SCOPE_DENIED` — the token lacks a required scope, or the tenant is below `x-askcv-min-tier`. Emitted by the route with the standard `ErrorEnvelope`; the shared agent-auth ENTRY gate emits the leaner `AuthErrorEnvelope` for a wrong-credential-type token, so both shapes are documented.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/ErrorEnvelope"
                    },
                    {
                      "$ref": "#/components/schemas/AuthErrorEnvelope"
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "description": "`NOT_FOUND` (unknown or non-exported capability) or `FEATURE_UNAVAILABLE` (currently launch-hidden).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "422": {
            "description": "`TOOL_FAILED` — the capability ran and reported a failure.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded — either the shared per-tenant surface budget or, when this operation declares `x-askcv-rate-limit`, its own tighter per-capability budget (charged on the same key over every transport). The budget really was spent, so `retryAfterMs` is an honest hint. Body: `{ error, retryAfterMs }`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        },
        "x-askcv-side": "read",
        "x-askcv-min-tier": "free",
        "x-askcv-cost-class": "cheap",
        "x-askcv-required-scopes": [
          "tool:coverage.getGaps"
        ],
        "x-askcv-coarse-scopes": [
          [
            "agent:query"
          ],
          [
            "agent:read"
          ]
        ]
      }
    },
    "/api/v1/capabilities/evidence.capture": {
      "post": {
        "operationId": "capability_evidence_capture",
        "summary": "evidence.capture",
        "description": "Log a structured accomplishment (STAR/CAR) to the user's knowledge base during a coaching or interview-prep conversation. Requires a metric or an explicit confirmation that none exists. Capture only what the user actually said — re-capturing identical evidence is a no-op. GRANT PROVENANCE decides the outcome: reached via an explicit `tool:<name>` grant on a token that does NOT also hold `agent:write`, this executes inline and returns 200; reached via the coarse `agent:write` grant (including a token holding both), it is staged for owner approval and returns 202.",
        "tags": [
          "writes"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "input": {
                    "$schema": "https://json-schema.org/draft/2020-12/schema",
                    "type": "object",
                    "properties": {
                      "situation": {
                        "type": "string",
                        "minLength": 1,
                        "maxLength": 2000
                      },
                      "action": {
                        "type": "string",
                        "minLength": 1,
                        "maxLength": 2000
                      },
                      "result": {
                        "type": "string",
                        "minLength": 1,
                        "maxLength": 2000
                      },
                      "metric": {
                        "description": "The quantified outcome, in the user's own words (e.g. '30% faster', '$2M saved').",
                        "type": "string",
                        "minLength": 1,
                        "maxLength": 500
                      },
                      "metricUnknown": {
                        "description": "Set true ONLY when the user has confirmed there is no metric — never as a default.",
                        "type": "boolean"
                      },
                      "attribution": {
                        "type": "string",
                        "enum": [
                          "led",
                          "contributed",
                          "team"
                        ],
                        "description": "Who gets credit: led it, contributed to it, or a team effort."
                      },
                      "occurredAt": {
                        "type": "string",
                        "format": "date-time",
                        "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
                        "description": "ISO8601 instant for when the accomplishment happened."
                      },
                      "artifactUrl": {
                        "type": "string",
                        "format": "uri"
                      },
                      "scopeSignals": {
                        "type": "object",
                        "properties": {
                          "teamSize": {
                            "type": "integer",
                            "exclusiveMinimum": 0,
                            "maximum": 9007199254740991
                          },
                          "blastRadius": {
                            "type": "string",
                            "enum": [
                              "self",
                              "team",
                              "org",
                              "company"
                            ]
                          }
                        },
                        "additionalProperties": false
                      }
                    },
                    "required": [
                      "situation",
                      "action",
                      "result",
                      "attribution",
                      "occurredAt"
                    ],
                    "additionalProperties": false
                  }
                },
                "required": [
                  "input"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Executed. The body is the capability's own result envelope plus `requestId`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "202": {
            "description": "Staged for owner approval (the grant was coarse, not an explicit per-capability grant). Body: `{ status: \"staged\", approvalId, requestId }`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "`VALIDATION_ERROR` (input failed the capability's schema), `IDEMPOTENCY_KEY_REQUIRED`, or `INVALID_JSON` (unparseable request body).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, expired, or revoked bearer token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "`SCOPE_DENIED` — the token lacks a required scope, or the tenant is below `x-askcv-min-tier`. Emitted by the route with the standard `ErrorEnvelope`; the shared agent-auth ENTRY gate emits the leaner `AuthErrorEnvelope` for a wrong-credential-type token, so both shapes are documented.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/ErrorEnvelope"
                    },
                    {
                      "$ref": "#/components/schemas/AuthErrorEnvelope"
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "description": "`NOT_FOUND` (unknown or non-exported capability) or `FEATURE_UNAVAILABLE` (currently launch-hidden).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "`STAGING_CONFLICT` — an identical staged action already exists. `retryable` is honest about whether retrying could change anything: `false` when the prior action was already DECIDED (the content-derived key means an unchanged retry lands on the same terminal row), `true` when it is still in flight.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "422": {
            "description": "`TOOL_FAILED` — the capability ran and reported a failure.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded — either the shared per-tenant surface budget or, when this operation declares `x-askcv-rate-limit`, its own tighter per-capability budget (charged on the same key over every transport). The budget really was spent, so `retryAfterMs` is an honest hint. Body: `{ error, retryAfterMs }`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        },
        "x-askcv-side": "write",
        "x-askcv-min-tier": "free",
        "x-askcv-cost-class": "cheap",
        "x-askcv-required-scopes": [
          "tool:evidence.capture"
        ],
        "x-askcv-coarse-scopes": [
          [
            "agent:write"
          ]
        ],
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Required intent marker. Its VALUE is never looked up: dedupe is content-derived from the validated input, so an identical payload dedupes regardless of this header, and a reused key with a changed payload executes as a new write."
          }
        ]
      }
    },
    "/api/v1/capabilities/export.getDomain": {
      "post": {
        "operationId": "capability_export_getDomain",
        "summary": "export.getDomain",
        "description": "Export one domain of the user's existing career data (experience, education, skills, projects, certifications, knowledge, coaching notes, and more), cursor-paginated. Use this to check what already exists before submitting an import, so you don't propose duplicates.",
        "tags": [
          "reads"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "input": {
                    "$schema": "https://json-schema.org/draft/2020-12/schema",
                    "type": "object",
                    "properties": {
                      "domain": {
                        "type": "string",
                        "enum": [
                          "knowledge",
                          "experience",
                          "education",
                          "skill",
                          "project",
                          "certification",
                          "performance_review",
                          "pipeline_job",
                          "coaching_note",
                          "journal_entry",
                          "profile_update"
                        ],
                        "description": "Which career-data domain to export."
                      },
                      "cursor": {
                        "description": "The `nextCursor` from the previous page. Omit for the first page.",
                        "anyOf": [
                          {
                            "type": "string",
                            "format": "uuid",
                            "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
                          },
                          {
                            "type": "null"
                          }
                        ]
                      },
                      "limit": {
                        "default": 50,
                        "description": "Items per page (1-100). Default 50.",
                        "type": "integer",
                        "minimum": 1,
                        "maximum": 100
                      }
                    },
                    "required": [
                      "domain",
                      "limit"
                    ],
                    "additionalProperties": false
                  }
                },
                "required": [
                  "input"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The capability's own result envelope plus `requestId`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "`VALIDATION_ERROR` (input failed the capability's schema) or `INVALID_JSON` (unparseable request body).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, expired, or revoked bearer token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "`SCOPE_DENIED` — the token lacks a required scope, or the tenant is below `x-askcv-min-tier`. Emitted by the route with the standard `ErrorEnvelope`; the shared agent-auth ENTRY gate emits the leaner `AuthErrorEnvelope` for a wrong-credential-type token, so both shapes are documented.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/ErrorEnvelope"
                    },
                    {
                      "$ref": "#/components/schemas/AuthErrorEnvelope"
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "description": "`NOT_FOUND` (unknown or non-exported capability) or `FEATURE_UNAVAILABLE` (currently launch-hidden).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "422": {
            "description": "`TOOL_FAILED` — the capability ran and reported a failure.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded — either the shared per-tenant surface budget or, when this operation declares `x-askcv-rate-limit`, its own tighter per-capability budget (charged on the same key over every transport). The budget really was spent, so `retryAfterMs` is an honest hint. Body: `{ error, retryAfterMs }`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        },
        "x-askcv-side": "read",
        "x-askcv-min-tier": "free",
        "x-askcv-cost-class": "cheap",
        "x-askcv-required-scopes": [
          "tool:export.getDomain"
        ],
        "x-askcv-coarse-scopes": [
          [
            "agent:query"
          ],
          [
            "agent:read"
          ]
        ]
      }
    },
    "/api/v1/capabilities/export.getProfile": {
      "post": {
        "operationId": "capability_export_getProfile",
        "summary": "export.getProfile",
        "description": "Export the user's profile record — name, title, headline, summary, location, photo, social links, agent identity, chat greeting, booking URL, and career story. An explicit column allow-list: never email, phone, confidential-search settings, or career-intent signals. Use `export.getDomain` for the associated experience, education, skills, and certifications.",
        "tags": [
          "reads"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "input": {
                    "$schema": "https://json-schema.org/draft/2020-12/schema",
                    "type": "object",
                    "properties": {},
                    "additionalProperties": false
                  }
                },
                "required": [
                  "input"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The capability's own result envelope plus `requestId`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "`VALIDATION_ERROR` (input failed the capability's schema) or `INVALID_JSON` (unparseable request body).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, expired, or revoked bearer token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "`SCOPE_DENIED` — the token lacks a required scope, or the tenant is below `x-askcv-min-tier`. Emitted by the route with the standard `ErrorEnvelope`; the shared agent-auth ENTRY gate emits the leaner `AuthErrorEnvelope` for a wrong-credential-type token, so both shapes are documented.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/ErrorEnvelope"
                    },
                    {
                      "$ref": "#/components/schemas/AuthErrorEnvelope"
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "description": "`NOT_FOUND` (unknown or non-exported capability) or `FEATURE_UNAVAILABLE` (currently launch-hidden).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "422": {
            "description": "`TOOL_FAILED` — the capability ran and reported a failure.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded — either the shared per-tenant surface budget or, when this operation declares `x-askcv-rate-limit`, its own tighter per-capability budget (charged on the same key over every transport). The budget really was spent, so `retryAfterMs` is an honest hint. Body: `{ error, retryAfterMs }`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        },
        "x-askcv-side": "read",
        "x-askcv-min-tier": "free",
        "x-askcv-cost-class": "cheap",
        "x-askcv-required-scopes": [
          "tool:export.getProfile"
        ],
        "x-askcv-coarse-scopes": [
          [
            "agent:query"
          ],
          [
            "agent:read"
          ]
        ]
      }
    },
    "/api/v1/capabilities/fit.check": {
      "post": {
        "operationId": "capability_fit_check",
        "summary": "fit.check",
        "description": "Score a job posting against the represented person's published career data. Returns an overall fit score, per-category breakdowns for skills, experience and qualifications, keyword match rate, suggestions, and interview topics. Requires agent:query scope and a Plus subscription; rate limited to 10 checks per hour.",
        "tags": [
          "reads"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "input": {
                    "$schema": "https://json-schema.org/draft/2020-12/schema",
                    "type": "object",
                    "properties": {
                      "jobTitle": {
                        "type": "string",
                        "minLength": 1,
                        "maxLength": 200,
                        "description": "Title of the job posting, e.g. 'Staff Product Manager'."
                      },
                      "jobDescription": {
                        "type": "string",
                        "minLength": 1,
                        "maxLength": 10000,
                        "description": "Full job description text to score the person against."
                      },
                      "company": {
                        "description": "Hiring company name. Defaults to 'Unknown' when omitted.",
                        "type": "string",
                        "maxLength": 200
                      }
                    },
                    "required": [
                      "jobTitle",
                      "jobDescription"
                    ],
                    "additionalProperties": false
                  }
                },
                "required": [
                  "input"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The capability's own result envelope plus `requestId`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "`VALIDATION_ERROR` (input failed the capability's schema) or `INVALID_JSON` (unparseable request body).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, expired, or revoked bearer token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "`SCOPE_DENIED` — the token lacks a required scope, or the tenant is below `x-askcv-min-tier`. Emitted by the route with the standard `ErrorEnvelope`; the shared agent-auth ENTRY gate emits the leaner `AuthErrorEnvelope` for a wrong-credential-type token, so both shapes are documented.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/ErrorEnvelope"
                    },
                    {
                      "$ref": "#/components/schemas/AuthErrorEnvelope"
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "description": "`NOT_FOUND` (unknown or non-exported capability) or `FEATURE_UNAVAILABLE` (currently launch-hidden).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "422": {
            "description": "`TOOL_FAILED` — the capability ran and reported a failure.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded — either the shared per-tenant surface budget or, when this operation declares `x-askcv-rate-limit`, its own tighter per-capability budget (charged on the same key over every transport). The budget really was spent, so `retryAfterMs` is an honest hint. Body: `{ error, retryAfterMs }`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "503": {
            "description": "`RATE_LIMITER_UNAVAILABLE` — rate limiting is temporarily unavailable and the request was refused defensively (a cost-bearing check fails CLOSED rather than letting an expensive call through unmetered). Distinct from 429: NO budget was consumed, so this is NOT a signal to back off against your quota. `retryable` is `true` and retrying shortly can succeed. Deliberately carries no `retryAfterMs`, because the limiter reports a full window on this path and quoting it would be a false retry hint.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "x-askcv-side": "read",
        "x-askcv-min-tier": "plus",
        "x-askcv-cost-class": "expensive",
        "x-askcv-required-scopes": [
          "tool:fit.check",
          "agent:query"
        ],
        "x-askcv-coarse-scopes": [],
        "x-askcv-rate-limit": {
          "perHour": 10,
          "scope": "tenant",
          "sharedAcrossTransports": true
        }
      }
    },
    "/api/v1/capabilities/framework.assessReadiness": {
      "post": {
        "operationId": "capability_framework_assessReadiness",
        "summary": "framework.assessReadiness",
        "description": "Assess whether the user is ready for their next career-framework level and surface the remaining gaps. Deterministic read over their framework, progression, and accumulated evidence — returns an overall readiness score (0-100), the next-level expectations they already meet, and the expectations still to close (with supporting evidence). Read-only; no token spend.",
        "tags": [
          "reads"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "input": {
                    "$schema": "https://json-schema.org/draft/2020-12/schema",
                    "type": "object",
                    "properties": {
                      "targetLevel": {
                        "description": "Optional target level code the user asked about (e.g. 'M5'). The assessment always reflects the target the user has set on their framework. Check the result's `requestedTargetMatch`: 'mismatched' → the answer is for `nextLevel`, not the level they named; 'ambiguous_track' → that code exists on both the IC and management tracks, so ask which one they mean.",
                        "type": "string",
                        "minLength": 1,
                        "maxLength": 120
                      }
                    },
                    "additionalProperties": false
                  }
                },
                "required": [
                  "input"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The capability's own result envelope plus `requestId`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "`VALIDATION_ERROR` (input failed the capability's schema) or `INVALID_JSON` (unparseable request body).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, expired, or revoked bearer token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "`SCOPE_DENIED` — the token lacks a required scope, or the tenant is below `x-askcv-min-tier`. Emitted by the route with the standard `ErrorEnvelope`; the shared agent-auth ENTRY gate emits the leaner `AuthErrorEnvelope` for a wrong-credential-type token, so both shapes are documented.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/ErrorEnvelope"
                    },
                    {
                      "$ref": "#/components/schemas/AuthErrorEnvelope"
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "description": "`NOT_FOUND` (unknown or non-exported capability) or `FEATURE_UNAVAILABLE` (currently launch-hidden).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "422": {
            "description": "`TOOL_FAILED` — the capability ran and reported a failure.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded — either the shared per-tenant surface budget or, when this operation declares `x-askcv-rate-limit`, its own tighter per-capability budget (charged on the same key over every transport). The budget really was spent, so `retryAfterMs` is an honest hint. Body: `{ error, retryAfterMs }`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        },
        "x-askcv-side": "read",
        "x-askcv-min-tier": "plus",
        "x-askcv-cost-class": "standard",
        "x-askcv-required-scopes": [
          "tool:framework.assessReadiness"
        ],
        "x-askcv-coarse-scopes": []
      }
    },
    "/api/v1/capabilities/framework.mapEvidence": {
      "post": {
        "operationId": "capability_framework_mapEvidence",
        "summary": "framework.mapEvidence",
        "description": "Map the user's banked evidence onto their confirmed career framework: which next-level expectations are already covered by evidence (hits), and which have nothing behind them at all (holes). Rows are per expectation CATEGORY, de-duplicated across the assessed level — NOT per expectation bullet, so holeCount is a count of uncovered categories, never of uncovered bullets. A category counts as a hit when at least one piece of evidence supports it, even if that evidence is still weak (such a row is a hit with strength 'gap' or 'developing'). Deterministic read over their framework, progression, and accumulated evidence — no token spend. Requires a confirmed framework; a staged proposal is not enough.",
        "tags": [
          "reads"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "input": {
                    "$schema": "https://json-schema.org/draft/2020-12/schema",
                    "type": "object",
                    "properties": {},
                    "additionalProperties": false
                  }
                },
                "required": [
                  "input"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The capability's own result envelope plus `requestId`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "`VALIDATION_ERROR` (input failed the capability's schema) or `INVALID_JSON` (unparseable request body).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, expired, or revoked bearer token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "`SCOPE_DENIED` — the token lacks a required scope, or the tenant is below `x-askcv-min-tier`. Emitted by the route with the standard `ErrorEnvelope`; the shared agent-auth ENTRY gate emits the leaner `AuthErrorEnvelope` for a wrong-credential-type token, so both shapes are documented.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/ErrorEnvelope"
                    },
                    {
                      "$ref": "#/components/schemas/AuthErrorEnvelope"
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "description": "`NOT_FOUND` (unknown or non-exported capability) or `FEATURE_UNAVAILABLE` (currently launch-hidden).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "422": {
            "description": "`TOOL_FAILED` — the capability ran and reported a failure.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded — either the shared per-tenant surface budget or, when this operation declares `x-askcv-rate-limit`, its own tighter per-capability budget (charged on the same key over every transport). The budget really was spent, so `retryAfterMs` is an honest hint. Body: `{ error, retryAfterMs }`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        },
        "x-askcv-side": "read",
        "x-askcv-min-tier": "plus",
        "x-askcv-cost-class": "standard",
        "x-askcv-required-scopes": [
          "tool:framework.mapEvidence"
        ],
        "x-askcv-coarse-scopes": []
      }
    },
    "/api/v1/capabilities/get_agent_depth": {
      "post": {
        "operationId": "capability_get_agent_depth",
        "summary": "get_agent_depth",
        "description": "Get the Agent Depth score — a 0-100 composite of how well the agent can represent the candidate. Includes component breakdown and recommendations. No AI call. Requires agent:query scope. Also callable as `depth.get`; this snake_case name is deprecated in favor of it.",
        "tags": [
          "reads"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "input": {
                    "$schema": "https://json-schema.org/draft/2020-12/schema",
                    "type": "object",
                    "properties": {},
                    "additionalProperties": false
                  }
                },
                "required": [
                  "input"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The capability's own result envelope plus `requestId`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "`VALIDATION_ERROR` (input failed the capability's schema) or `INVALID_JSON` (unparseable request body).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, expired, or revoked bearer token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "`SCOPE_DENIED` — the token lacks a required scope, or the tenant is below `x-askcv-min-tier`. Emitted by the route with the standard `ErrorEnvelope`; the shared agent-auth ENTRY gate emits the leaner `AuthErrorEnvelope` for a wrong-credential-type token, so both shapes are documented.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/ErrorEnvelope"
                    },
                    {
                      "$ref": "#/components/schemas/AuthErrorEnvelope"
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "description": "`NOT_FOUND` (unknown or non-exported capability) or `FEATURE_UNAVAILABLE` (currently launch-hidden).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "422": {
            "description": "`TOOL_FAILED` — the capability ran and reported a failure.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded — either the shared per-tenant surface budget or, when this operation declares `x-askcv-rate-limit`, its own tighter per-capability budget (charged on the same key over every transport). The budget really was spent, so `retryAfterMs` is an honest hint. Body: `{ error, retryAfterMs }`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        },
        "x-askcv-side": "read",
        "x-askcv-min-tier": "free",
        "x-askcv-cost-class": "cheap",
        "x-askcv-required-scopes": [
          "tool:get_agent_depth",
          "agent:query"
        ],
        "x-askcv-coarse-scopes": [
          [
            "agent:query"
          ]
        ],
        "x-askcv-aliases": [
          "depth.get"
        ]
      }
    },
    "/api/v1/capabilities/get_career_goals": {
      "post": {
        "operationId": "capability_get_career_goals",
        "summary": "get_career_goals",
        "description": "Get the user's career goals, primary target role, and all target roles. No AI call — reads from the database. Requires agent:query scope. Also callable as `goals.list`; this snake_case name is deprecated in favor of it.",
        "tags": [
          "reads"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "input": {
                    "$schema": "https://json-schema.org/draft/2020-12/schema",
                    "type": "object",
                    "properties": {},
                    "additionalProperties": false
                  }
                },
                "required": [
                  "input"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The capability's own result envelope plus `requestId`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "`VALIDATION_ERROR` (input failed the capability's schema) or `INVALID_JSON` (unparseable request body).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, expired, or revoked bearer token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "`SCOPE_DENIED` — the token lacks a required scope, or the tenant is below `x-askcv-min-tier`. Emitted by the route with the standard `ErrorEnvelope`; the shared agent-auth ENTRY gate emits the leaner `AuthErrorEnvelope` for a wrong-credential-type token, so both shapes are documented.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/ErrorEnvelope"
                    },
                    {
                      "$ref": "#/components/schemas/AuthErrorEnvelope"
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "description": "`NOT_FOUND` (unknown or non-exported capability) or `FEATURE_UNAVAILABLE` (currently launch-hidden).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "422": {
            "description": "`TOOL_FAILED` — the capability ran and reported a failure.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded — either the shared per-tenant surface budget or, when this operation declares `x-askcv-rate-limit`, its own tighter per-capability budget (charged on the same key over every transport). The budget really was spent, so `retryAfterMs` is an honest hint. Body: `{ error, retryAfterMs }`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        },
        "x-askcv-side": "read",
        "x-askcv-min-tier": "free",
        "x-askcv-cost-class": "cheap",
        "x-askcv-required-scopes": [
          "tool:get_career_goals",
          "agent:query"
        ],
        "x-askcv-coarse-scopes": [
          [
            "agent:query"
          ]
        ],
        "x-askcv-aliases": [
          "goals.list"
        ]
      }
    },
    "/api/v1/capabilities/get_coverage_score": {
      "post": {
        "operationId": "capability_get_coverage_score",
        "summary": "get_coverage_score",
        "description": "Get the coverage score for the user's primary target role — what percentage of requirements are covered by their knowledge base. Requires agent:query scope and Plus subscription. Also callable as `coverage.getScore`; this snake_case name is deprecated in favor of it.",
        "tags": [
          "reads"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "input": {
                    "$schema": "https://json-schema.org/draft/2020-12/schema",
                    "type": "object",
                    "properties": {},
                    "additionalProperties": false
                  }
                },
                "required": [
                  "input"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The capability's own result envelope plus `requestId`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "`VALIDATION_ERROR` (input failed the capability's schema) or `INVALID_JSON` (unparseable request body).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, expired, or revoked bearer token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "`SCOPE_DENIED` — the token lacks a required scope, or the tenant is below `x-askcv-min-tier`. Emitted by the route with the standard `ErrorEnvelope`; the shared agent-auth ENTRY gate emits the leaner `AuthErrorEnvelope` for a wrong-credential-type token, so both shapes are documented.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/ErrorEnvelope"
                    },
                    {
                      "$ref": "#/components/schemas/AuthErrorEnvelope"
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "description": "`NOT_FOUND` (unknown or non-exported capability) or `FEATURE_UNAVAILABLE` (currently launch-hidden).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "422": {
            "description": "`TOOL_FAILED` — the capability ran and reported a failure.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded — either the shared per-tenant surface budget or, when this operation declares `x-askcv-rate-limit`, its own tighter per-capability budget (charged on the same key over every transport). The budget really was spent, so `retryAfterMs` is an honest hint. Body: `{ error, retryAfterMs }`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        },
        "x-askcv-side": "read",
        "x-askcv-min-tier": "free",
        "x-askcv-cost-class": "standard",
        "x-askcv-required-scopes": [
          "tool:get_coverage_score",
          "agent:query"
        ],
        "x-askcv-coarse-scopes": [
          [
            "agent:query"
          ]
        ],
        "x-askcv-aliases": [
          "coverage.getScore"
        ]
      }
    },
    "/api/v1/capabilities/import.createBatch": {
      "post": {
        "operationId": "capability_import_createBatch",
        "summary": "import.createBatch",
        "description": "Submit a batch of career-data items (experience, education, skills, projects, certifications, knowledge, coaching notes, …) for the user to review. Items are validated per-domain and STAGED as pending — nothing enters the user's career data until they approve it in their import review queue. Returns the batch id, how many items were staged vs rejected, and the review link. Re-submitting an identical batch returns the original batch rather than creating a second one. GRANT PROVENANCE decides the outcome: reached via an explicit `tool:<name>` grant on a token that does NOT also hold `agent:write`, this executes inline and returns 200; reached via the coarse `agent:write` grant (including a token holding both), it is staged for owner approval and returns 202.",
        "tags": [
          "writes"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "input": {
                    "$schema": "https://json-schema.org/draft/2020-12/schema",
                    "type": "object",
                    "properties": {
                      "items": {
                        "minItems": 1,
                        "maxItems": 100,
                        "type": "array",
                        "items": {
                          "type": "object",
                          "properties": {
                            "domain": {
                              "type": "string",
                              "enum": [
                                "knowledge",
                                "experience",
                                "education",
                                "skill",
                                "project",
                                "certification",
                                "performance_review",
                                "pipeline_job",
                                "coaching_note",
                                "journal_entry",
                                "profile_update"
                              ],
                              "description": "Which career-data domain this item belongs to."
                            },
                            "payload": {
                              "type": "object",
                              "propertyNames": {
                                "type": "string"
                              },
                              "additionalProperties": {},
                              "description": "The item itself, matching that domain's schema. Call `describe_import_domain` (or read the capability's own schema) for the per-domain shape."
                            }
                          },
                          "required": [
                            "domain",
                            "payload"
                          ],
                          "additionalProperties": false
                        },
                        "description": "The items to stage for review (1-100). Items that fail their domain schema are reported in `errors` and simply not staged — the rest of the batch still lands."
                      },
                      "agentLabel": {
                        "description": "Human-readable name for your agent, shown on the review card.",
                        "type": "string",
                        "maxLength": 100
                      },
                      "agentId": {
                        "description": "Stable identifier for your agent, for the owner's audit trail.",
                        "type": "string",
                        "maxLength": 200
                      },
                      "contextNote": {
                        "description": "Why you are submitting this batch, in one or two sentences. Shown to the owner during review. Also the field to vary when you deliberately want a SECOND batch of otherwise identical items.",
                        "type": "string",
                        "maxLength": 1000
                      }
                    },
                    "required": [
                      "items"
                    ],
                    "additionalProperties": false
                  }
                },
                "required": [
                  "input"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Executed. The effect is a pending item in this capability's OWN review queue — a human still approves it there before anything user-visible happens. No approval card is staged and no 202 is ever returned; the envelope's own fields carry the pending state and any dedupe outcome. (This is what `x-askcv-self-staging` means.)",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "`VALIDATION_ERROR` (input failed the capability's schema), `IDEMPOTENCY_KEY_REQUIRED`, or `INVALID_JSON` (unparseable request body).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, expired, or revoked bearer token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "`SCOPE_DENIED` — the token lacks a required scope, or the tenant is below `x-askcv-min-tier`. Emitted by the route with the standard `ErrorEnvelope`; the shared agent-auth ENTRY gate emits the leaner `AuthErrorEnvelope` for a wrong-credential-type token, so both shapes are documented.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/ErrorEnvelope"
                    },
                    {
                      "$ref": "#/components/schemas/AuthErrorEnvelope"
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "description": "`NOT_FOUND` (unknown or non-exported capability) or `FEATURE_UNAVAILABLE` (currently launch-hidden).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "422": {
            "description": "`TOOL_FAILED` — the capability ran and reported a failure.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded — either the shared per-tenant surface budget or, when this operation declares `x-askcv-rate-limit`, its own tighter per-capability budget (charged on the same key over every transport). The budget really was spent, so `retryAfterMs` is an honest hint. Body: `{ error, retryAfterMs }`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "503": {
            "description": "`RATE_LIMITER_UNAVAILABLE` — rate limiting is temporarily unavailable and the request was refused defensively (a cost-bearing check fails CLOSED rather than letting an expensive call through unmetered). Distinct from 429: NO budget was consumed, so this is NOT a signal to back off against your quota. `retryable` is `true` and retrying shortly can succeed. Deliberately carries no `retryAfterMs`, because the limiter reports a full window on this path and quoting it would be a false retry hint.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          }
        },
        "x-askcv-side": "write",
        "x-askcv-min-tier": "free",
        "x-askcv-cost-class": "cheap",
        "x-askcv-required-scopes": [
          "tool:import.createBatch"
        ],
        "x-askcv-coarse-scopes": [
          [
            "agent:write"
          ]
        ],
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Required intent marker. Its VALUE is never looked up: dedupe is content-derived from the validated input, so an identical payload dedupes regardless of this header, and a reused key with a changed payload executes as a new write."
          }
        ],
        "x-askcv-rate-limit": {
          "perHour": 20,
          "scope": "tenant",
          "sharedAcrossTransports": true
        },
        "x-askcv-self-staging": true
      }
    },
    "/api/v1/capabilities/import.getBatchStatus": {
      "post": {
        "operationId": "capability_import_getBatchStatus",
        "summary": "import.getBatchStatus",
        "description": "Check a submitted import batch: overall review status, how many items are pending / approved / rejected, and each item's preview plus any reviewer note or failure reason.",
        "tags": [
          "reads"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "input": {
                    "$schema": "https://json-schema.org/draft/2020-12/schema",
                    "type": "object",
                    "properties": {
                      "batchId": {
                        "type": "string",
                        "format": "uuid",
                        "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
                        "description": "The batch id returned by `import.createBatch`."
                      }
                    },
                    "required": [
                      "batchId"
                    ],
                    "additionalProperties": false
                  }
                },
                "required": [
                  "input"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The capability's own result envelope plus `requestId`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "`VALIDATION_ERROR` (input failed the capability's schema) or `INVALID_JSON` (unparseable request body).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, expired, or revoked bearer token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "`SCOPE_DENIED` — the token lacks a required scope, or the tenant is below `x-askcv-min-tier`. Emitted by the route with the standard `ErrorEnvelope`; the shared agent-auth ENTRY gate emits the leaner `AuthErrorEnvelope` for a wrong-credential-type token, so both shapes are documented.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/ErrorEnvelope"
                    },
                    {
                      "$ref": "#/components/schemas/AuthErrorEnvelope"
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "description": "`NOT_FOUND` (unknown or non-exported capability) or `FEATURE_UNAVAILABLE` (currently launch-hidden).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "422": {
            "description": "`TOOL_FAILED` — the capability ran and reported a failure.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded — either the shared per-tenant surface budget or, when this operation declares `x-askcv-rate-limit`, its own tighter per-capability budget (charged on the same key over every transport). The budget really was spent, so `retryAfterMs` is an honest hint. Body: `{ error, retryAfterMs }`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        },
        "x-askcv-side": "read",
        "x-askcv-min-tier": "free",
        "x-askcv-cost-class": "cheap",
        "x-askcv-required-scopes": [
          "tool:import.getBatchStatus"
        ],
        "x-askcv-coarse-scopes": [
          [
            "agent:query"
          ],
          [
            "agent:read"
          ]
        ]
      }
    },
    "/api/v1/capabilities/interview.listSessions": {
      "post": {
        "operationId": "capability_interview_listSessions",
        "summary": "interview.listSessions",
        "description": "List the user's interview sessions (mock + real + reference + debrief), newest first. Optional jobId filter narrows to a single target job. Paginated — pair with interview-specific tools for deeper detail.",
        "tags": [
          "reads"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "input": {
                    "$schema": "https://json-schema.org/draft/2020-12/schema",
                    "type": "object",
                    "properties": {
                      "jobId": {
                        "description": "Narrow to interviews attached to this target_job id.",
                        "type": "string",
                        "format": "uuid",
                        "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
                      },
                      "cursor": {
                        "description": "Opaque pagination cursor from a prior response. Omit for the first page.",
                        "anyOf": [
                          {
                            "type": "string"
                          },
                          {
                            "type": "null"
                          }
                        ]
                      },
                      "limit": {
                        "default": 20,
                        "description": "Maximum items to return in this page (1-50). Default 20.",
                        "type": "integer",
                        "minimum": 1,
                        "maximum": 50
                      }
                    },
                    "required": [
                      "limit"
                    ],
                    "additionalProperties": false
                  }
                },
                "required": [
                  "input"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The capability's own result envelope plus `requestId`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "`VALIDATION_ERROR` (input failed the capability's schema) or `INVALID_JSON` (unparseable request body).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, expired, or revoked bearer token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "`SCOPE_DENIED` — the token lacks a required scope, or the tenant is below `x-askcv-min-tier`. Emitted by the route with the standard `ErrorEnvelope`; the shared agent-auth ENTRY gate emits the leaner `AuthErrorEnvelope` for a wrong-credential-type token, so both shapes are documented.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/ErrorEnvelope"
                    },
                    {
                      "$ref": "#/components/schemas/AuthErrorEnvelope"
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "description": "`NOT_FOUND` (unknown or non-exported capability) or `FEATURE_UNAVAILABLE` (currently launch-hidden).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "422": {
            "description": "`TOOL_FAILED` — the capability ran and reported a failure.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded — either the shared per-tenant surface budget or, when this operation declares `x-askcv-rate-limit`, its own tighter per-capability budget (charged on the same key over every transport). The budget really was spent, so `retryAfterMs` is an honest hint. Body: `{ error, retryAfterMs }`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        },
        "x-askcv-side": "read",
        "x-askcv-min-tier": "free",
        "x-askcv-cost-class": "cheap",
        "x-askcv-required-scopes": [
          "tool:interview.listSessions"
        ],
        "x-askcv-coarse-scopes": []
      }
    },
    "/api/v1/capabilities/journal.captureEntry": {
      "post": {
        "operationId": "capability_journal_captureEntry",
        "summary": "journal.captureEntry",
        "description": "Log a weekly win or reflection into the user's knowledge base during a coaching or counseling conversation. The entry feeds future retrieval the same way the Sunday weekly-win check-in does. Reversible within the 30s undo window (undo retracts the created entry). Capture only what the user actually said — each DISTINCT win is saved as its own entry (call once per win); re-capturing a win that is already saved for this week is a no-op. GRANT PROVENANCE decides the outcome: reached via an explicit `tool:<name>` grant on a token that does NOT also hold `agent:write`, this executes inline and returns 200; reached via the coarse `agent:write` grant (including a token holding both), it is staged for owner approval and returns 202.",
        "tags": [
          "writes"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "input": {
                    "$schema": "https://json-schema.org/draft/2020-12/schema",
                    "type": "object",
                    "properties": {
                      "content": {
                        "type": "string",
                        "minLength": 1,
                        "maxLength": 2000,
                        "description": "The weekly win or reflection in the user's own words. 1–3 sentences. Examples: 'Shipped the Q2 pricing migration two days early and the team called it the smoothest rollout this year.' or 'Realised I default to saying yes — practiced declining one low-value meeting.'"
                      }
                    },
                    "required": [
                      "content"
                    ],
                    "additionalProperties": false
                  }
                },
                "required": [
                  "input"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Executed. The body is the capability's own result envelope plus `requestId`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "202": {
            "description": "Staged for owner approval (the grant was coarse, not an explicit per-capability grant). Body: `{ status: \"staged\", approvalId, requestId }`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "`VALIDATION_ERROR` (input failed the capability's schema), `IDEMPOTENCY_KEY_REQUIRED`, or `INVALID_JSON` (unparseable request body).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, expired, or revoked bearer token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "`SCOPE_DENIED` — the token lacks a required scope, or the tenant is below `x-askcv-min-tier`. Emitted by the route with the standard `ErrorEnvelope`; the shared agent-auth ENTRY gate emits the leaner `AuthErrorEnvelope` for a wrong-credential-type token, so both shapes are documented.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/ErrorEnvelope"
                    },
                    {
                      "$ref": "#/components/schemas/AuthErrorEnvelope"
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "description": "`NOT_FOUND` (unknown or non-exported capability) or `FEATURE_UNAVAILABLE` (currently launch-hidden).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "`STAGING_CONFLICT` — an identical staged action already exists. `retryable` is honest about whether retrying could change anything: `false` when the prior action was already DECIDED (the content-derived key means an unchanged retry lands on the same terminal row), `true` when it is still in flight.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "422": {
            "description": "`TOOL_FAILED` — the capability ran and reported a failure.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded — either the shared per-tenant surface budget or, when this operation declares `x-askcv-rate-limit`, its own tighter per-capability budget (charged on the same key over every transport). The budget really was spent, so `retryAfterMs` is an honest hint. Body: `{ error, retryAfterMs }`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        },
        "x-askcv-side": "write",
        "x-askcv-min-tier": "free",
        "x-askcv-cost-class": "cheap",
        "x-askcv-required-scopes": [
          "tool:journal.captureEntry"
        ],
        "x-askcv-coarse-scopes": [
          [
            "agent:write"
          ]
        ],
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Required intent marker. Its VALUE is never looked up: dedupe is content-derived from the validated input, so an identical payload dedupes regardless of this header, and a reused key with a changed payload executes as a new write."
          }
        ]
      }
    },
    "/api/v1/capabilities/kb.addEntry": {
      "post": {
        "operationId": "capability_kb_addEntry",
        "summary": "kb.addEntry",
        "description": "Add a new knowledge-base entry with a question + answer (and optional tags). Every write lands an agent_actions row — the caller receives an undo token valid for 30 seconds. GRANT PROVENANCE decides the outcome: reached via an explicit `tool:<name>` grant on a token that does NOT also hold `agent:write`, this executes inline and returns 200; reached via the coarse `agent:write` grant (including a token holding both), it is staged for owner approval and returns 202.",
        "tags": [
          "writes"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "input": {
                    "$schema": "https://json-schema.org/draft/2020-12/schema",
                    "type": "object",
                    "properties": {
                      "question": {
                        "type": "string",
                        "minLength": 1,
                        "maxLength": 500,
                        "description": "The question this entry answers."
                      },
                      "answer": {
                        "type": "string",
                        "minLength": 1,
                        "maxLength": 10000,
                        "description": "The answer content."
                      },
                      "tags": {
                        "description": "Optional tags.",
                        "maxItems": 20,
                        "type": "array",
                        "items": {
                          "type": "string",
                          "maxLength": 60
                        }
                      },
                      "source": {
                        "default": "manual_entry",
                        "description": "Source of the entry.",
                        "type": "string",
                        "enum": [
                          "mock_interview",
                          "manual_entry",
                          "resume_import",
                          "document_upload",
                          "linkedin_import",
                          "url_import",
                          "github_import"
                        ]
                      }
                    },
                    "required": [
                      "question",
                      "answer",
                      "source"
                    ],
                    "additionalProperties": false
                  }
                },
                "required": [
                  "input"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Executed. The body is the capability's own result envelope plus `requestId`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "202": {
            "description": "Staged for owner approval (the grant was coarse, not an explicit per-capability grant). Body: `{ status: \"staged\", approvalId, requestId }`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "`VALIDATION_ERROR` (input failed the capability's schema), `IDEMPOTENCY_KEY_REQUIRED`, or `INVALID_JSON` (unparseable request body).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, expired, or revoked bearer token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "`SCOPE_DENIED` — the token lacks a required scope, or the tenant is below `x-askcv-min-tier`. Emitted by the route with the standard `ErrorEnvelope`; the shared agent-auth ENTRY gate emits the leaner `AuthErrorEnvelope` for a wrong-credential-type token, so both shapes are documented.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/ErrorEnvelope"
                    },
                    {
                      "$ref": "#/components/schemas/AuthErrorEnvelope"
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "description": "`NOT_FOUND` (unknown or non-exported capability) or `FEATURE_UNAVAILABLE` (currently launch-hidden).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "`STAGING_CONFLICT` — an identical staged action already exists. `retryable` is honest about whether retrying could change anything: `false` when the prior action was already DECIDED (the content-derived key means an unchanged retry lands on the same terminal row), `true` when it is still in flight.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "422": {
            "description": "`TOOL_FAILED` — the capability ran and reported a failure.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded — either the shared per-tenant surface budget or, when this operation declares `x-askcv-rate-limit`, its own tighter per-capability budget (charged on the same key over every transport). The budget really was spent, so `retryAfterMs` is an honest hint. Body: `{ error, retryAfterMs }`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        },
        "x-askcv-side": "write",
        "x-askcv-min-tier": "plus",
        "x-askcv-cost-class": "standard",
        "x-askcv-required-scopes": [
          "tool:kb.addEntry"
        ],
        "x-askcv-coarse-scopes": [
          [
            "agent:write"
          ]
        ],
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Required intent marker. Its VALUE is never looked up: dedupe is content-derived from the validated input, so an identical payload dedupes regardless of this header, and a reused key with a changed payload executes as a new write."
          }
        ]
      }
    },
    "/api/v1/capabilities/kb.find": {
      "post": {
        "operationId": "capability_kb_find",
        "summary": "kb.find",
        "description": "Paginated plaintext search over the user's knowledge base. Returns enumerable KbEntryBrief rows — use kb.getEntry to fetch full content. Visibility is capped to the calling agent's max.",
        "tags": [
          "reads"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "input": {
                    "$schema": "https://json-schema.org/draft/2020-12/schema",
                    "type": "object",
                    "properties": {
                      "query": {
                        "type": "string",
                        "minLength": 1,
                        "maxLength": 500,
                        "description": "Plain-text search query. Matched against answer text."
                      },
                      "cursor": {
                        "description": "Opaque pagination cursor from a prior response. Omit for the first page.",
                        "anyOf": [
                          {
                            "type": "string"
                          },
                          {
                            "type": "null"
                          }
                        ]
                      },
                      "limit": {
                        "default": 20,
                        "description": "Maximum items to return in this page (1-50). Default 20.",
                        "type": "integer",
                        "minimum": 1,
                        "maximum": 50
                      }
                    },
                    "required": [
                      "query",
                      "limit"
                    ],
                    "additionalProperties": false
                  }
                },
                "required": [
                  "input"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The capability's own result envelope plus `requestId`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "`VALIDATION_ERROR` (input failed the capability's schema) or `INVALID_JSON` (unparseable request body).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, expired, or revoked bearer token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "`SCOPE_DENIED` — the token lacks a required scope, or the tenant is below `x-askcv-min-tier`. Emitted by the route with the standard `ErrorEnvelope`; the shared agent-auth ENTRY gate emits the leaner `AuthErrorEnvelope` for a wrong-credential-type token, so both shapes are documented.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/ErrorEnvelope"
                    },
                    {
                      "$ref": "#/components/schemas/AuthErrorEnvelope"
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "description": "`NOT_FOUND` (unknown or non-exported capability) or `FEATURE_UNAVAILABLE` (currently launch-hidden).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "422": {
            "description": "`TOOL_FAILED` — the capability ran and reported a failure.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded — either the shared per-tenant surface budget or, when this operation declares `x-askcv-rate-limit`, its own tighter per-capability budget (charged on the same key over every transport). The budget really was spent, so `retryAfterMs` is an honest hint. Body: `{ error, retryAfterMs }`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        },
        "x-askcv-side": "read",
        "x-askcv-min-tier": "free",
        "x-askcv-cost-class": "cheap",
        "x-askcv-required-scopes": [
          "tool:kb.find"
        ],
        "x-askcv-coarse-scopes": []
      }
    },
    "/api/v1/capabilities/kb.getEntry": {
      "post": {
        "operationId": "capability_kb_getEntry",
        "summary": "kb.getEntry",
        "description": "Fetch full knowledge-base entries by one `id` or batch 1-20 `ids`. Batch related reads in one call instead of repeating the tool. Batch results are keyed by requested id and report missing/hidden entries per id. Tenancy + visibility are enforced server-side; entries the caller cannot see surface as not_found (never forbidden).",
        "tags": [
          "reads"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "input": {
                    "$schema": "https://json-schema.org/draft/2020-12/schema",
                    "type": "object",
                    "properties": {
                      "id": {
                        "description": "One knowledge entry UUID.",
                        "type": "string",
                        "format": "uuid",
                        "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
                      },
                      "ids": {
                        "description": "Batch of 1-20 knowledge entry UUIDs. Prefer this over repeated single-id calls.",
                        "minItems": 1,
                        "maxItems": 20,
                        "type": "array",
                        "items": {
                          "type": "string",
                          "format": "uuid",
                          "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
                        }
                      }
                    },
                    "additionalProperties": false
                  }
                },
                "required": [
                  "input"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The capability's own result envelope plus `requestId`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "`VALIDATION_ERROR` (input failed the capability's schema) or `INVALID_JSON` (unparseable request body).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, expired, or revoked bearer token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "`SCOPE_DENIED` — the token lacks a required scope, or the tenant is below `x-askcv-min-tier`. Emitted by the route with the standard `ErrorEnvelope`; the shared agent-auth ENTRY gate emits the leaner `AuthErrorEnvelope` for a wrong-credential-type token, so both shapes are documented.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/ErrorEnvelope"
                    },
                    {
                      "$ref": "#/components/schemas/AuthErrorEnvelope"
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "description": "`NOT_FOUND` (unknown or non-exported capability) or `FEATURE_UNAVAILABLE` (currently launch-hidden).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "422": {
            "description": "`TOOL_FAILED` — the capability ran and reported a failure.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded — either the shared per-tenant surface budget or, when this operation declares `x-askcv-rate-limit`, its own tighter per-capability budget (charged on the same key over every transport). The budget really was spent, so `retryAfterMs` is an honest hint. Body: `{ error, retryAfterMs }`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        },
        "x-askcv-side": "read",
        "x-askcv-min-tier": "free",
        "x-askcv-cost-class": "cheap",
        "x-askcv-required-scopes": [
          "tool:kb.getEntry"
        ],
        "x-askcv-coarse-scopes": []
      }
    },
    "/api/v1/capabilities/kb.updateEntry": {
      "post": {
        "operationId": "capability_kb_updateEntry",
        "summary": "kb.updateEntry",
        "description": "Patch question / answer / tags on an existing knowledge-base entry. Reversible within the 30s undo window — the harness snapshots the prior state into `undoPayload`. GRANT PROVENANCE decides the outcome: reached via an explicit `tool:<name>` grant on a token that does NOT also hold `agent:write`, this executes inline and returns 200; reached via the coarse `agent:write` grant (including a token holding both), it is staged for owner approval and returns 202.",
        "tags": [
          "writes"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "input": {
                    "$schema": "https://json-schema.org/draft/2020-12/schema",
                    "type": "object",
                    "properties": {
                      "id": {
                        "type": "string",
                        "format": "uuid",
                        "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
                        "description": "Entry ID to update."
                      },
                      "question": {
                        "type": "string",
                        "minLength": 1,
                        "maxLength": 500
                      },
                      "answer": {
                        "type": "string",
                        "minLength": 1,
                        "maxLength": 10000
                      },
                      "tags": {
                        "anyOf": [
                          {
                            "maxItems": 20,
                            "type": "array",
                            "items": {
                              "type": "string",
                              "maxLength": 60
                            }
                          },
                          {
                            "type": "null"
                          }
                        ]
                      }
                    },
                    "required": [
                      "id"
                    ],
                    "additionalProperties": false
                  }
                },
                "required": [
                  "input"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Executed. The body is the capability's own result envelope plus `requestId`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "202": {
            "description": "Staged for owner approval (the grant was coarse, not an explicit per-capability grant). Body: `{ status: \"staged\", approvalId, requestId }`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "`VALIDATION_ERROR` (input failed the capability's schema), `IDEMPOTENCY_KEY_REQUIRED`, or `INVALID_JSON` (unparseable request body).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, expired, or revoked bearer token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "`SCOPE_DENIED` — the token lacks a required scope, or the tenant is below `x-askcv-min-tier`. Emitted by the route with the standard `ErrorEnvelope`; the shared agent-auth ENTRY gate emits the leaner `AuthErrorEnvelope` for a wrong-credential-type token, so both shapes are documented.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/ErrorEnvelope"
                    },
                    {
                      "$ref": "#/components/schemas/AuthErrorEnvelope"
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "description": "`NOT_FOUND` (unknown or non-exported capability) or `FEATURE_UNAVAILABLE` (currently launch-hidden).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "`STAGING_CONFLICT` — an identical staged action already exists. `retryable` is honest about whether retrying could change anything: `false` when the prior action was already DECIDED (the content-derived key means an unchanged retry lands on the same terminal row), `true` when it is still in flight.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "422": {
            "description": "`TOOL_FAILED` — the capability ran and reported a failure.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded — either the shared per-tenant surface budget or, when this operation declares `x-askcv-rate-limit`, its own tighter per-capability budget (charged on the same key over every transport). The budget really was spent, so `retryAfterMs` is an honest hint. Body: `{ error, retryAfterMs }`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        },
        "x-askcv-side": "write",
        "x-askcv-min-tier": "plus",
        "x-askcv-cost-class": "standard",
        "x-askcv-required-scopes": [
          "tool:kb.updateEntry"
        ],
        "x-askcv-coarse-scopes": [
          [
            "agent:write"
          ]
        ],
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Required intent marker. Its VALUE is never looked up: dedupe is content-derived from the validated input, so an identical payload dedupes regardless of this header, and a reused key with a changed payload executes as a new write."
          }
        ]
      }
    },
    "/api/v1/capabilities/notifications.cancelReminder": {
      "post": {
        "operationId": "capability_notifications_cancelReminder",
        "summary": "notifications.cancelReminder",
        "description": "Cancel one of the user's own pending follow-up reminders by id (from notifications.listReminders). Returns cancelled:false (with an explanatory message) if the reminder was already delivered or cancelled — this is NOT an error, and is non-reversible (nothing changed, so there is nothing to undo). A genuine cancel IS reversible within 30s on inline execution — undo restores the reminder to pending. To reschedule a reminder for a different time, cancel it and call notifications.scheduleReminder again with the new time (there is no separate 'snooze' tool). GRANT PROVENANCE decides the outcome: reached via an explicit `tool:<name>` grant on a token that does NOT also hold `agent:write`, this executes inline and returns 200; reached via the coarse `agent:write` grant (including a token holding both), it is staged for owner approval and returns 202.",
        "tags": [
          "writes"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "input": {
                    "$schema": "https://json-schema.org/draft/2020-12/schema",
                    "type": "object",
                    "properties": {
                      "id": {
                        "type": "string",
                        "format": "uuid",
                        "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
                        "description": "The reminder's id, as returned by notifications.listReminders."
                      }
                    },
                    "required": [
                      "id"
                    ],
                    "additionalProperties": false
                  }
                },
                "required": [
                  "input"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Executed. The body is the capability's own result envelope plus `requestId`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "202": {
            "description": "Staged for owner approval (the grant was coarse, not an explicit per-capability grant). Body: `{ status: \"staged\", approvalId, requestId }`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "`VALIDATION_ERROR` (input failed the capability's schema), `IDEMPOTENCY_KEY_REQUIRED`, or `INVALID_JSON` (unparseable request body).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, expired, or revoked bearer token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "`SCOPE_DENIED` — the token lacks a required scope, or the tenant is below `x-askcv-min-tier`. Emitted by the route with the standard `ErrorEnvelope`; the shared agent-auth ENTRY gate emits the leaner `AuthErrorEnvelope` for a wrong-credential-type token, so both shapes are documented.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/ErrorEnvelope"
                    },
                    {
                      "$ref": "#/components/schemas/AuthErrorEnvelope"
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "description": "`NOT_FOUND` (unknown or non-exported capability) or `FEATURE_UNAVAILABLE` (currently launch-hidden).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "`STAGING_CONFLICT` — an identical staged action already exists. `retryable` is honest about whether retrying could change anything: `false` when the prior action was already DECIDED (the content-derived key means an unchanged retry lands on the same terminal row), `true` when it is still in flight.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "422": {
            "description": "`TOOL_FAILED` — the capability ran and reported a failure.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded — either the shared per-tenant surface budget or, when this operation declares `x-askcv-rate-limit`, its own tighter per-capability budget (charged on the same key over every transport). The budget really was spent, so `retryAfterMs` is an honest hint. Body: `{ error, retryAfterMs }`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        },
        "x-askcv-side": "write",
        "x-askcv-min-tier": "free",
        "x-askcv-cost-class": "cheap",
        "x-askcv-required-scopes": [
          "tool:notifications.cancelReminder"
        ],
        "x-askcv-coarse-scopes": [
          [
            "agent:write"
          ]
        ],
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Required intent marker. Its VALUE is never looked up: dedupe is content-derived from the validated input, so an identical payload dedupes regardless of this header, and a reused key with a changed payload executes as a new write."
          }
        ]
      }
    },
    "/api/v1/capabilities/notifications.listReminders": {
      "post": {
        "operationId": "capability_notifications_listReminders",
        "summary": "notifications.listReminders",
        "description": "List the user's pending follow-up reminders (not yet delivered, not cancelled), soonest first. Use this to check what's already scheduled before setting a new one, or to help the user review/cancel an existing reminder.",
        "tags": [
          "reads"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "input": {
                    "$schema": "https://json-schema.org/draft/2020-12/schema",
                    "type": "object",
                    "properties": {},
                    "additionalProperties": false
                  }
                },
                "required": [
                  "input"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The capability's own result envelope plus `requestId`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "`VALIDATION_ERROR` (input failed the capability's schema) or `INVALID_JSON` (unparseable request body).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, expired, or revoked bearer token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "`SCOPE_DENIED` — the token lacks a required scope, or the tenant is below `x-askcv-min-tier`. Emitted by the route with the standard `ErrorEnvelope`; the shared agent-auth ENTRY gate emits the leaner `AuthErrorEnvelope` for a wrong-credential-type token, so both shapes are documented.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/ErrorEnvelope"
                    },
                    {
                      "$ref": "#/components/schemas/AuthErrorEnvelope"
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "description": "`NOT_FOUND` (unknown or non-exported capability) or `FEATURE_UNAVAILABLE` (currently launch-hidden).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "422": {
            "description": "`TOOL_FAILED` — the capability ran and reported a failure.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded — either the shared per-tenant surface budget or, when this operation declares `x-askcv-rate-limit`, its own tighter per-capability budget (charged on the same key over every transport). The budget really was spent, so `retryAfterMs` is an honest hint. Body: `{ error, retryAfterMs }`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        },
        "x-askcv-side": "read",
        "x-askcv-min-tier": "free",
        "x-askcv-cost-class": "cheap",
        "x-askcv-required-scopes": [
          "tool:notifications.listReminders"
        ],
        "x-askcv-coarse-scopes": []
      }
    },
    "/api/v1/capabilities/notifications.scheduleReminder": {
      "post": {
        "operationId": "capability_notifications_scheduleReminder",
        "summary": "notifications.scheduleReminder",
        "description": "Set a follow-up nudge for the user (e.g. a post-interview check-in) during a coaching or counseling conversation. Sends to the user's notification inbox IMMEDIATELY when scheduledAt is omitted, or schedules delivery (up to 365 days out, landing within a few minutes of the requested time, not instantly — even for a near-now or already-past time) when scheduledAt is provided. Use this when the user agrees to be reminded to follow up on something. An IMMEDIATE send is non-reversible (a delivered notification cannot be unsent). A SCHEDULED reminder is reversible within 30s on inline execution — undo cancels the pending reminder, the same effect as notifications.cancelReminder — after that window, cancel it directly. GRANT PROVENANCE decides the outcome: reached via an explicit `tool:<name>` grant on a token that does NOT also hold `agent:write`, this executes inline and returns 200; reached via the coarse `agent:write` grant (including a token holding both), it is staged for owner approval and returns 202.",
        "tags": [
          "writes"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "input": {
                    "$schema": "https://json-schema.org/draft/2020-12/schema",
                    "type": "object",
                    "properties": {
                      "message": {
                        "type": "string",
                        "minLength": 1,
                        "maxLength": 300,
                        "description": "The reminder text in the user's own framing — what to follow up on and why it matters. 1–2 sentences. Example: 'Check in on how the Acme final-round went and capture what you learned while it's fresh.'"
                      },
                      "reminderType": {
                        "type": "string",
                        "enum": [
                          "general",
                          "post_interview",
                          "weekly_reflection"
                        ],
                        "description": "Which kind of follow-up this nudge is. 'post_interview' for a post-interview check-in, 'weekly_reflection' to prompt a weekly win, or 'general' (default) for any other follow-up."
                      },
                      "scheduledAt": {
                        "description": "OPTIONAL ISO-8601 timestamp for WHEN to fire. Omit this to send the nudge immediately. Pass any timestamp (up to 365 days out) to schedule it for later — the reminder will land in the user's inbox within a few minutes of that time (not instantly), even if the requested time is now or already in the past.",
                        "type": "string",
                        "format": "date-time",
                        "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
                      }
                    },
                    "required": [
                      "message"
                    ],
                    "additionalProperties": false
                  }
                },
                "required": [
                  "input"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Executed. The body is the capability's own result envelope plus `requestId`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "202": {
            "description": "Staged for owner approval (the grant was coarse, not an explicit per-capability grant). Body: `{ status: \"staged\", approvalId, requestId }`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "`VALIDATION_ERROR` (input failed the capability's schema), `IDEMPOTENCY_KEY_REQUIRED`, or `INVALID_JSON` (unparseable request body).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, expired, or revoked bearer token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "`SCOPE_DENIED` — the token lacks a required scope, or the tenant is below `x-askcv-min-tier`. Emitted by the route with the standard `ErrorEnvelope`; the shared agent-auth ENTRY gate emits the leaner `AuthErrorEnvelope` for a wrong-credential-type token, so both shapes are documented.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/ErrorEnvelope"
                    },
                    {
                      "$ref": "#/components/schemas/AuthErrorEnvelope"
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "description": "`NOT_FOUND` (unknown or non-exported capability) or `FEATURE_UNAVAILABLE` (currently launch-hidden).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "`STAGING_CONFLICT` — an identical staged action already exists. `retryable` is honest about whether retrying could change anything: `false` when the prior action was already DECIDED (the content-derived key means an unchanged retry lands on the same terminal row), `true` when it is still in flight.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "422": {
            "description": "`TOOL_FAILED` — the capability ran and reported a failure.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded — either the shared per-tenant surface budget or, when this operation declares `x-askcv-rate-limit`, its own tighter per-capability budget (charged on the same key over every transport). The budget really was spent, so `retryAfterMs` is an honest hint. Body: `{ error, retryAfterMs }`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        },
        "x-askcv-side": "write",
        "x-askcv-min-tier": "free",
        "x-askcv-cost-class": "cheap",
        "x-askcv-required-scopes": [
          "tool:notifications.scheduleReminder"
        ],
        "x-askcv-coarse-scopes": [
          [
            "agent:write"
          ]
        ],
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Required intent marker. Its VALUE is never looked up: dedupe is content-derived from the validated input, so an identical payload dedupes regardless of this header, and a reused key with a changed payload executes as a new write."
          }
        ]
      }
    },
    "/api/v1/capabilities/pipeline.addJob": {
      "post": {
        "operationId": "capability_pipeline_addJob",
        "summary": "pipeline.addJob",
        "description": "Add a new job to the pipeline. When `roleId` is omitted, the tenant's primary target role is used as the parent. Reversible — undo deletes the created row. GRANT PROVENANCE decides the outcome: reached via an explicit `tool:<name>` grant on a token that does NOT also hold `agent:write`, this executes inline and returns 200; reached via the coarse `agent:write` grant (including a token holding both), it is staged for owner approval and returns 202.",
        "tags": [
          "writes"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "input": {
                    "$schema": "https://json-schema.org/draft/2020-12/schema",
                    "type": "object",
                    "properties": {
                      "company": {
                        "type": "string",
                        "minLength": 1,
                        "maxLength": 200,
                        "description": "Employer name."
                      },
                      "title": {
                        "description": "Job title.",
                        "anyOf": [
                          {
                            "type": "string",
                            "maxLength": 300
                          },
                          {
                            "type": "null"
                          }
                        ]
                      },
                      "roleId": {
                        "description": "Target role this job rolls up to. When omitted, the primary target role is used — which fails if the tenant has none.",
                        "type": "string",
                        "format": "uuid",
                        "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
                      },
                      "sourceUrl": {
                        "anyOf": [
                          {
                            "type": "string",
                            "maxLength": 2000
                          },
                          {
                            "type": "null"
                          }
                        ]
                      },
                      "status": {
                        "description": "Initial funnel status. Defaults to `saved`.",
                        "type": "string",
                        "enum": [
                          "saved",
                          "applied",
                          "screening",
                          "interviewing",
                          "offer",
                          "rejected",
                          "withdrawn",
                          "skipped"
                        ]
                      },
                      "notes": {
                        "anyOf": [
                          {
                            "type": "string",
                            "maxLength": 5000
                          },
                          {
                            "type": "null"
                          }
                        ]
                      },
                      "description": {
                        "anyOf": [
                          {
                            "type": "string",
                            "maxLength": 20000
                          },
                          {
                            "type": "null"
                          }
                        ]
                      }
                    },
                    "required": [
                      "company"
                    ],
                    "additionalProperties": false
                  }
                },
                "required": [
                  "input"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Executed. The body is the capability's own result envelope plus `requestId`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "202": {
            "description": "Staged for owner approval (the grant was coarse, not an explicit per-capability grant). Body: `{ status: \"staged\", approvalId, requestId }`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "`VALIDATION_ERROR` (input failed the capability's schema), `IDEMPOTENCY_KEY_REQUIRED`, or `INVALID_JSON` (unparseable request body).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, expired, or revoked bearer token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "`SCOPE_DENIED` — the token lacks a required scope, or the tenant is below `x-askcv-min-tier`. Emitted by the route with the standard `ErrorEnvelope`; the shared agent-auth ENTRY gate emits the leaner `AuthErrorEnvelope` for a wrong-credential-type token, so both shapes are documented.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/ErrorEnvelope"
                    },
                    {
                      "$ref": "#/components/schemas/AuthErrorEnvelope"
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "description": "`NOT_FOUND` (unknown or non-exported capability) or `FEATURE_UNAVAILABLE` (currently launch-hidden).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "`STAGING_CONFLICT` — an identical staged action already exists. `retryable` is honest about whether retrying could change anything: `false` when the prior action was already DECIDED (the content-derived key means an unchanged retry lands on the same terminal row), `true` when it is still in flight.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "422": {
            "description": "`TOOL_FAILED` — the capability ran and reported a failure.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded — either the shared per-tenant surface budget or, when this operation declares `x-askcv-rate-limit`, its own tighter per-capability budget (charged on the same key over every transport). The budget really was spent, so `retryAfterMs` is an honest hint. Body: `{ error, retryAfterMs }`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        },
        "x-askcv-side": "write",
        "x-askcv-min-tier": "free",
        "x-askcv-cost-class": "cheap",
        "x-askcv-required-scopes": [
          "tool:pipeline.addJob"
        ],
        "x-askcv-coarse-scopes": [
          [
            "agent:write"
          ]
        ],
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Required intent marker. Its VALUE is never looked up: dedupe is content-derived from the validated input, so an identical payload dedupes regardless of this header, and a reused key with a changed payload executes as a new write."
          }
        ]
      }
    },
    "/api/v1/capabilities/pipeline.getJob": {
      "post": {
        "operationId": "capability_pipeline_getJob",
        "summary": "pipeline.getJob",
        "description": "Fetch a single target job with enough context (status, role, next interview, notes) for the supervisor to reason about next actions. Tenant-scoped; non-existent / cross-tenant ids return not_found.",
        "tags": [
          "reads"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "input": {
                    "$schema": "https://json-schema.org/draft/2020-12/schema",
                    "type": "object",
                    "properties": {
                      "id": {
                        "type": "string",
                        "format": "uuid",
                        "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
                        "description": "Target-job UUID."
                      }
                    },
                    "required": [
                      "id"
                    ],
                    "additionalProperties": false
                  }
                },
                "required": [
                  "input"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The capability's own result envelope plus `requestId`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "`VALIDATION_ERROR` (input failed the capability's schema) or `INVALID_JSON` (unparseable request body).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, expired, or revoked bearer token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "`SCOPE_DENIED` — the token lacks a required scope, or the tenant is below `x-askcv-min-tier`. Emitted by the route with the standard `ErrorEnvelope`; the shared agent-auth ENTRY gate emits the leaner `AuthErrorEnvelope` for a wrong-credential-type token, so both shapes are documented.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/ErrorEnvelope"
                    },
                    {
                      "$ref": "#/components/schemas/AuthErrorEnvelope"
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "description": "`NOT_FOUND` (unknown or non-exported capability) or `FEATURE_UNAVAILABLE` (currently launch-hidden).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "422": {
            "description": "`TOOL_FAILED` — the capability ran and reported a failure.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded — either the shared per-tenant surface budget or, when this operation declares `x-askcv-rate-limit`, its own tighter per-capability budget (charged on the same key over every transport). The budget really was spent, so `retryAfterMs` is an honest hint. Body: `{ error, retryAfterMs }`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        },
        "x-askcv-side": "read",
        "x-askcv-min-tier": "free",
        "x-askcv-cost-class": "standard",
        "x-askcv-required-scopes": [
          "tool:pipeline.getJob"
        ],
        "x-askcv-coarse-scopes": []
      }
    },
    "/api/v1/capabilities/pipeline.listJobs": {
      "post": {
        "operationId": "capability_pipeline_listJobs",
        "summary": "pipeline.listJobs",
        "description": "List the user's target jobs, optionally filtered by application status. Paginated — use cursor + limit to walk the pipeline. Pair with pipeline.getJob for full job details.",
        "tags": [
          "reads"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "input": {
                    "$schema": "https://json-schema.org/draft/2020-12/schema",
                    "type": "object",
                    "properties": {
                      "status": {
                        "description": "Filter by application status.",
                        "type": "string",
                        "enum": [
                          "saved",
                          "applied",
                          "screening",
                          "interviewing",
                          "offer",
                          "rejected",
                          "withdrawn",
                          "skipped"
                        ]
                      },
                      "cursor": {
                        "description": "Opaque pagination cursor from a prior response. Omit for the first page.",
                        "anyOf": [
                          {
                            "type": "string"
                          },
                          {
                            "type": "null"
                          }
                        ]
                      },
                      "limit": {
                        "default": 20,
                        "description": "Maximum items to return in this page (1-50). Default 20.",
                        "type": "integer",
                        "minimum": 1,
                        "maximum": 50
                      }
                    },
                    "required": [
                      "limit"
                    ],
                    "additionalProperties": false
                  }
                },
                "required": [
                  "input"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The capability's own result envelope plus `requestId`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "`VALIDATION_ERROR` (input failed the capability's schema) or `INVALID_JSON` (unparseable request body).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, expired, or revoked bearer token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "`SCOPE_DENIED` — the token lacks a required scope, or the tenant is below `x-askcv-min-tier`. Emitted by the route with the standard `ErrorEnvelope`; the shared agent-auth ENTRY gate emits the leaner `AuthErrorEnvelope` for a wrong-credential-type token, so both shapes are documented.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/ErrorEnvelope"
                    },
                    {
                      "$ref": "#/components/schemas/AuthErrorEnvelope"
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "description": "`NOT_FOUND` (unknown or non-exported capability) or `FEATURE_UNAVAILABLE` (currently launch-hidden).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "422": {
            "description": "`TOOL_FAILED` — the capability ran and reported a failure.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded — either the shared per-tenant surface budget or, when this operation declares `x-askcv-rate-limit`, its own tighter per-capability budget (charged on the same key over every transport). The budget really was spent, so `retryAfterMs` is an honest hint. Body: `{ error, retryAfterMs }`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        },
        "x-askcv-side": "read",
        "x-askcv-min-tier": "free",
        "x-askcv-cost-class": "standard",
        "x-askcv-required-scopes": [
          "tool:pipeline.listJobs"
        ],
        "x-askcv-coarse-scopes": []
      }
    },
    "/api/v1/capabilities/pipeline.logInterview": {
      "post": {
        "operationId": "capability_pipeline_logInterview",
        "summary": "pipeline.logInterview",
        "description": "Stamp a scheduled interview date + stage on a pipeline job. Optionally advances the funnel status to `interviewing` when the job is earlier in the pipeline. Reversible — the prior date / stage / status land in `undoPayload`. GRANT PROVENANCE decides the outcome: reached via an explicit `tool:<name>` grant on a token that does NOT also hold `agent:write`, this executes inline and returns 200; reached via the coarse `agent:write` grant (including a token holding both), it is staged for owner approval and returns 202.",
        "tags": [
          "writes"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "input": {
                    "$schema": "https://json-schema.org/draft/2020-12/schema",
                    "type": "object",
                    "properties": {
                      "jobId": {
                        "type": "string",
                        "format": "uuid",
                        "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
                      },
                      "interviewDate": {
                        "type": "string",
                        "format": "date-time",
                        "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
                        "description": "ISO8601 timestamp for the interview."
                      },
                      "interviewStage": {
                        "type": "string",
                        "enum": [
                          "recruiter_screen",
                          "phone_screen",
                          "hiring_manager",
                          "technical",
                          "case_study",
                          "panel",
                          "presentation",
                          "final",
                          "other"
                        ],
                        "description": "Interview stage (phone_screen, hiring_manager, …)."
                      },
                      "note": {
                        "anyOf": [
                          {
                            "type": "string",
                            "maxLength": 2000
                          },
                          {
                            "type": "null"
                          }
                        ]
                      },
                      "advanceStatus": {
                        "type": "boolean"
                      }
                    },
                    "required": [
                      "jobId",
                      "interviewDate",
                      "interviewStage"
                    ],
                    "additionalProperties": false
                  }
                },
                "required": [
                  "input"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Executed. The body is the capability's own result envelope plus `requestId`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "202": {
            "description": "Staged for owner approval (the grant was coarse, not an explicit per-capability grant). Body: `{ status: \"staged\", approvalId, requestId }`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "`VALIDATION_ERROR` (input failed the capability's schema), `IDEMPOTENCY_KEY_REQUIRED`, or `INVALID_JSON` (unparseable request body).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, expired, or revoked bearer token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "`SCOPE_DENIED` — the token lacks a required scope, or the tenant is below `x-askcv-min-tier`. Emitted by the route with the standard `ErrorEnvelope`; the shared agent-auth ENTRY gate emits the leaner `AuthErrorEnvelope` for a wrong-credential-type token, so both shapes are documented.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/ErrorEnvelope"
                    },
                    {
                      "$ref": "#/components/schemas/AuthErrorEnvelope"
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "description": "`NOT_FOUND` (unknown or non-exported capability) or `FEATURE_UNAVAILABLE` (currently launch-hidden).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "`STAGING_CONFLICT` — an identical staged action already exists. `retryable` is honest about whether retrying could change anything: `false` when the prior action was already DECIDED (the content-derived key means an unchanged retry lands on the same terminal row), `true` when it is still in flight.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "422": {
            "description": "`TOOL_FAILED` — the capability ran and reported a failure.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded — either the shared per-tenant surface budget or, when this operation declares `x-askcv-rate-limit`, its own tighter per-capability budget (charged on the same key over every transport). The budget really was spent, so `retryAfterMs` is an honest hint. Body: `{ error, retryAfterMs }`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        },
        "x-askcv-side": "write",
        "x-askcv-min-tier": "free",
        "x-askcv-cost-class": "cheap",
        "x-askcv-required-scopes": [
          "tool:pipeline.logInterview"
        ],
        "x-askcv-coarse-scopes": [
          [
            "agent:write"
          ]
        ],
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Required intent marker. Its VALUE is never looked up: dedupe is content-derived from the validated input, so an identical payload dedupes regardless of this header, and a reused key with a changed payload executes as a new write."
          }
        ]
      }
    },
    "/api/v1/capabilities/pipeline.updateStatus": {
      "post": {
        "operationId": "capability_pipeline_updateStatus",
        "summary": "pipeline.updateStatus",
        "description": "Move a pipeline job between funnel statuses (saved → applied → screening → interviewing → offer, or → rejected / withdrawn / skipped). `skipped` is reserved for the user's pre-application filter — agents should not auto-skip a job; only set it when the user has explicitly declined to pursue. Reversible — the prior status is captured on `undoPayload`. GRANT PROVENANCE decides the outcome: reached via an explicit `tool:<name>` grant on a token that does NOT also hold `agent:write`, this executes inline and returns 200; reached via the coarse `agent:write` grant (including a token holding both), it is staged for owner approval and returns 202.",
        "tags": [
          "writes"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "input": {
                    "$schema": "https://json-schema.org/draft/2020-12/schema",
                    "type": "object",
                    "properties": {
                      "jobId": {
                        "type": "string",
                        "format": "uuid",
                        "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
                        "description": "Pipeline job ID."
                      },
                      "status": {
                        "type": "string",
                        "enum": [
                          "saved",
                          "applied",
                          "screening",
                          "interviewing",
                          "offer",
                          "rejected",
                          "withdrawn",
                          "skipped"
                        ],
                        "description": "New funnel status."
                      },
                      "note": {
                        "description": "Optional note appended to the job on status change.",
                        "anyOf": [
                          {
                            "type": "string",
                            "maxLength": 2000
                          },
                          {
                            "type": "null"
                          }
                        ]
                      }
                    },
                    "required": [
                      "jobId",
                      "status"
                    ],
                    "additionalProperties": false
                  }
                },
                "required": [
                  "input"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Executed. The body is the capability's own result envelope plus `requestId`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "202": {
            "description": "Staged for owner approval (the grant was coarse, not an explicit per-capability grant). Body: `{ status: \"staged\", approvalId, requestId }`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "`VALIDATION_ERROR` (input failed the capability's schema), `IDEMPOTENCY_KEY_REQUIRED`, or `INVALID_JSON` (unparseable request body).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, expired, or revoked bearer token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "`SCOPE_DENIED` — the token lacks a required scope, or the tenant is below `x-askcv-min-tier`. Emitted by the route with the standard `ErrorEnvelope`; the shared agent-auth ENTRY gate emits the leaner `AuthErrorEnvelope` for a wrong-credential-type token, so both shapes are documented.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/ErrorEnvelope"
                    },
                    {
                      "$ref": "#/components/schemas/AuthErrorEnvelope"
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "description": "`NOT_FOUND` (unknown or non-exported capability) or `FEATURE_UNAVAILABLE` (currently launch-hidden).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "`STAGING_CONFLICT` — an identical staged action already exists. `retryable` is honest about whether retrying could change anything: `false` when the prior action was already DECIDED (the content-derived key means an unchanged retry lands on the same terminal row), `true` when it is still in flight.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "422": {
            "description": "`TOOL_FAILED` — the capability ran and reported a failure.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded — either the shared per-tenant surface budget or, when this operation declares `x-askcv-rate-limit`, its own tighter per-capability budget (charged on the same key over every transport). The budget really was spent, so `retryAfterMs` is an honest hint. Body: `{ error, retryAfterMs }`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        },
        "x-askcv-side": "write",
        "x-askcv-min-tier": "free",
        "x-askcv-cost-class": "cheap",
        "x-askcv-required-scopes": [
          "tool:pipeline.updateStatus"
        ],
        "x-askcv-coarse-scopes": [
          [
            "agent:write"
          ]
        ],
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Required intent marker. Its VALUE is never looked up: dedupe is content-derived from the validated input, so an identical payload dedupes regardless of this header, and a reused key with a changed payload executes as a new write."
          }
        ]
      }
    },
    "/api/v1/capabilities/profile.addExperience": {
      "post": {
        "operationId": "capability_profile_addExperience",
        "summary": "profile.addExperience",
        "description": "Add a new experience row to the user's resume. Reversible — undo deletes the created row. GRANT PROVENANCE decides the outcome: reached via an explicit `tool:<name>` grant on a token that does NOT also hold `agent:write`, this executes inline and returns 200; reached via the coarse `agent:write` grant (including a token holding both), it is staged for owner approval and returns 202.",
        "tags": [
          "writes"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "input": {
                    "$schema": "https://json-schema.org/draft/2020-12/schema",
                    "type": "object",
                    "properties": {
                      "company": {
                        "type": "string",
                        "minLength": 1,
                        "maxLength": 200,
                        "description": "Employer name."
                      },
                      "role": {
                        "type": "string",
                        "minLength": 1,
                        "maxLength": 200,
                        "description": "Job title."
                      },
                      "location": {
                        "anyOf": [
                          {
                            "type": "string",
                            "maxLength": 200
                          },
                          {
                            "type": "null"
                          }
                        ]
                      },
                      "startDate": {
                        "type": "string",
                        "pattern": "^\\d{4}-\\d{2}-\\d{2}$",
                        "description": "Start date (YYYY-MM-DD)."
                      },
                      "endDate": {
                        "description": "End date (YYYY-MM-DD) — null for current role.",
                        "anyOf": [
                          {
                            "type": "string",
                            "pattern": "^\\d{4}-\\d{2}-\\d{2}$"
                          },
                          {
                            "type": "null"
                          }
                        ]
                      },
                      "description": {
                        "anyOf": [
                          {
                            "type": "string",
                            "maxLength": 5000
                          },
                          {
                            "type": "null"
                          }
                        ]
                      },
                      "bullets": {
                        "anyOf": [
                          {
                            "maxItems": 20,
                            "type": "array",
                            "items": {
                              "type": "string",
                              "minLength": 1,
                              "maxLength": 500
                            }
                          },
                          {
                            "type": "null"
                          }
                        ]
                      }
                    },
                    "required": [
                      "company",
                      "role",
                      "startDate"
                    ],
                    "additionalProperties": false
                  }
                },
                "required": [
                  "input"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Executed. The body is the capability's own result envelope plus `requestId`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "202": {
            "description": "Staged for owner approval (the grant was coarse, not an explicit per-capability grant). Body: `{ status: \"staged\", approvalId, requestId }`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "`VALIDATION_ERROR` (input failed the capability's schema), `IDEMPOTENCY_KEY_REQUIRED`, or `INVALID_JSON` (unparseable request body).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, expired, or revoked bearer token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "`SCOPE_DENIED` — the token lacks a required scope, or the tenant is below `x-askcv-min-tier`. Emitted by the route with the standard `ErrorEnvelope`; the shared agent-auth ENTRY gate emits the leaner `AuthErrorEnvelope` for a wrong-credential-type token, so both shapes are documented.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/ErrorEnvelope"
                    },
                    {
                      "$ref": "#/components/schemas/AuthErrorEnvelope"
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "description": "`NOT_FOUND` (unknown or non-exported capability) or `FEATURE_UNAVAILABLE` (currently launch-hidden).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "`STAGING_CONFLICT` — an identical staged action already exists. `retryable` is honest about whether retrying could change anything: `false` when the prior action was already DECIDED (the content-derived key means an unchanged retry lands on the same terminal row), `true` when it is still in flight.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "422": {
            "description": "`TOOL_FAILED` — the capability ran and reported a failure.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded — either the shared per-tenant surface budget or, when this operation declares `x-askcv-rate-limit`, its own tighter per-capability budget (charged on the same key over every transport). The budget really was spent, so `retryAfterMs` is an honest hint. Body: `{ error, retryAfterMs }`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        },
        "x-askcv-side": "write",
        "x-askcv-min-tier": "free",
        "x-askcv-cost-class": "cheap",
        "x-askcv-required-scopes": [
          "tool:profile.addExperience"
        ],
        "x-askcv-coarse-scopes": [
          [
            "agent:write"
          ]
        ],
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Required intent marker. Its VALUE is never looked up: dedupe is content-derived from the validated input, so an identical payload dedupes regardless of this header, and a reused key with a changed payload executes as a new write."
          }
        ]
      }
    },
    "/api/v1/capabilities/profile.get": {
      "post": {
        "operationId": "capability_profile_get",
        "summary": "profile.get",
        "description": "Fetch a redacted ProfileDigest for the current tenant. Output is an explicit allow-list (name, headline, location, title, primary stage, target roles, public-profile flag) — never email, phone, or internal ids.",
        "tags": [
          "reads"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "input": {
                    "$schema": "https://json-schema.org/draft/2020-12/schema",
                    "type": "object",
                    "properties": {},
                    "additionalProperties": false
                  }
                },
                "required": [
                  "input"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The capability's own result envelope plus `requestId`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "`VALIDATION_ERROR` (input failed the capability's schema) or `INVALID_JSON` (unparseable request body).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, expired, or revoked bearer token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "`SCOPE_DENIED` — the token lacks a required scope, or the tenant is below `x-askcv-min-tier`. Emitted by the route with the standard `ErrorEnvelope`; the shared agent-auth ENTRY gate emits the leaner `AuthErrorEnvelope` for a wrong-credential-type token, so both shapes are documented.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/ErrorEnvelope"
                    },
                    {
                      "$ref": "#/components/schemas/AuthErrorEnvelope"
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "description": "`NOT_FOUND` (unknown or non-exported capability) or `FEATURE_UNAVAILABLE` (currently launch-hidden).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "422": {
            "description": "`TOOL_FAILED` — the capability ran and reported a failure.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded — either the shared per-tenant surface budget or, when this operation declares `x-askcv-rate-limit`, its own tighter per-capability budget (charged on the same key over every transport). The budget really was spent, so `retryAfterMs` is an honest hint. Body: `{ error, retryAfterMs }`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        },
        "x-askcv-side": "read",
        "x-askcv-min-tier": "free",
        "x-askcv-cost-class": "cheap",
        "x-askcv-required-scopes": [
          "tool:profile.get"
        ],
        "x-askcv-coarse-scopes": []
      }
    },
    "/api/v1/capabilities/profile.updateHeadline": {
      "post": {
        "operationId": "capability_profile_updateHeadline",
        "summary": "profile.updateHeadline",
        "description": "Replace the user's profile headline. Reversible — the prior headline is captured on `undoPayload` so the 30s undo restores it. GRANT PROVENANCE decides the outcome: reached via an explicit `tool:<name>` grant on a token that does NOT also hold `agent:write`, this executes inline and returns 200; reached via the coarse `agent:write` grant (including a token holding both), it is staged for owner approval and returns 202.",
        "tags": [
          "writes"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "input": {
                    "$schema": "https://json-schema.org/draft/2020-12/schema",
                    "type": "object",
                    "properties": {
                      "headline": {
                        "type": "string",
                        "minLength": 1,
                        "maxLength": 200,
                        "description": "New headline (1–200 chars). Omit newlines."
                      }
                    },
                    "required": [
                      "headline"
                    ],
                    "additionalProperties": false
                  }
                },
                "required": [
                  "input"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Executed. The body is the capability's own result envelope plus `requestId`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "202": {
            "description": "Staged for owner approval (the grant was coarse, not an explicit per-capability grant). Body: `{ status: \"staged\", approvalId, requestId }`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "`VALIDATION_ERROR` (input failed the capability's schema), `IDEMPOTENCY_KEY_REQUIRED`, or `INVALID_JSON` (unparseable request body).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, expired, or revoked bearer token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "`SCOPE_DENIED` — the token lacks a required scope, or the tenant is below `x-askcv-min-tier`. Emitted by the route with the standard `ErrorEnvelope`; the shared agent-auth ENTRY gate emits the leaner `AuthErrorEnvelope` for a wrong-credential-type token, so both shapes are documented.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/ErrorEnvelope"
                    },
                    {
                      "$ref": "#/components/schemas/AuthErrorEnvelope"
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "description": "`NOT_FOUND` (unknown or non-exported capability) or `FEATURE_UNAVAILABLE` (currently launch-hidden).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "`STAGING_CONFLICT` — an identical staged action already exists. `retryable` is honest about whether retrying could change anything: `false` when the prior action was already DECIDED (the content-derived key means an unchanged retry lands on the same terminal row), `true` when it is still in flight.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "422": {
            "description": "`TOOL_FAILED` — the capability ran and reported a failure.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded — either the shared per-tenant surface budget or, when this operation declares `x-askcv-rate-limit`, its own tighter per-capability budget (charged on the same key over every transport). The budget really was spent, so `retryAfterMs` is an honest hint. Body: `{ error, retryAfterMs }`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        },
        "x-askcv-side": "write",
        "x-askcv-min-tier": "free",
        "x-askcv-cost-class": "cheap",
        "x-askcv-required-scopes": [
          "tool:profile.updateHeadline"
        ],
        "x-askcv-coarse-scopes": [
          [
            "agent:write"
          ]
        ],
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Required intent marker. Its VALUE is never looked up: dedupe is content-derived from the validated input, so an identical payload dedupes regardless of this header, and a reused key with a changed payload executes as a new write."
          }
        ]
      }
    },
    "/api/v1/capabilities/profile.updateSummary": {
      "post": {
        "operationId": "capability_profile_updateSummary",
        "summary": "profile.updateSummary",
        "description": "Replace the user's profile summary / bio. Reversible — the prior summary is captured on `undoPayload`. GRANT PROVENANCE decides the outcome: reached via an explicit `tool:<name>` grant on a token that does NOT also hold `agent:write`, this executes inline and returns 200; reached via the coarse `agent:write` grant (including a token holding both), it is staged for owner approval and returns 202.",
        "tags": [
          "writes"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "input": {
                    "$schema": "https://json-schema.org/draft/2020-12/schema",
                    "type": "object",
                    "properties": {
                      "summary": {
                        "type": "string",
                        "minLength": 1,
                        "maxLength": 5000,
                        "description": "New summary (1–5000 chars). Plain text; markdown links allowed."
                      }
                    },
                    "required": [
                      "summary"
                    ],
                    "additionalProperties": false
                  }
                },
                "required": [
                  "input"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Executed. The body is the capability's own result envelope plus `requestId`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "202": {
            "description": "Staged for owner approval (the grant was coarse, not an explicit per-capability grant). Body: `{ status: \"staged\", approvalId, requestId }`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "`VALIDATION_ERROR` (input failed the capability's schema), `IDEMPOTENCY_KEY_REQUIRED`, or `INVALID_JSON` (unparseable request body).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, expired, or revoked bearer token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "`SCOPE_DENIED` — the token lacks a required scope, or the tenant is below `x-askcv-min-tier`. Emitted by the route with the standard `ErrorEnvelope`; the shared agent-auth ENTRY gate emits the leaner `AuthErrorEnvelope` for a wrong-credential-type token, so both shapes are documented.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/ErrorEnvelope"
                    },
                    {
                      "$ref": "#/components/schemas/AuthErrorEnvelope"
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "description": "`NOT_FOUND` (unknown or non-exported capability) or `FEATURE_UNAVAILABLE` (currently launch-hidden).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "409": {
            "description": "`STAGING_CONFLICT` — an identical staged action already exists. `retryable` is honest about whether retrying could change anything: `false` when the prior action was already DECIDED (the content-derived key means an unchanged retry lands on the same terminal row), `true` when it is still in flight.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "422": {
            "description": "`TOOL_FAILED` — the capability ran and reported a failure.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded — either the shared per-tenant surface budget or, when this operation declares `x-askcv-rate-limit`, its own tighter per-capability budget (charged on the same key over every transport). The budget really was spent, so `retryAfterMs` is an honest hint. Body: `{ error, retryAfterMs }`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        },
        "x-askcv-side": "write",
        "x-askcv-min-tier": "free",
        "x-askcv-cost-class": "cheap",
        "x-askcv-required-scopes": [
          "tool:profile.updateSummary"
        ],
        "x-askcv-coarse-scopes": [
          [
            "agent:write"
          ]
        ],
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Required intent marker. Its VALUE is never looked up: dedupe is content-derived from the validated input, so an identical payload dedupes regardless of this header, and a reused key with a changed payload executes as a new write."
          }
        ]
      }
    },
    "/api/v1/capabilities/search_evidence": {
      "post": {
        "operationId": "capability_search_evidence",
        "summary": "search_evidence",
        "description": "Search the user's knowledge base for evidence matching a requirement. Returns ranked entries with relevance scores. Requires agent:query scope. Also callable as `evidence.search`; this snake_case name is deprecated in favor of it.",
        "tags": [
          "reads"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "input": {
                    "$schema": "https://json-schema.org/draft/2020-12/schema",
                    "type": "object",
                    "properties": {
                      "requirement": {
                        "type": "string",
                        "minLength": 1,
                        "maxLength": 500,
                        "description": "The requirement to search evidence for."
                      }
                    },
                    "required": [
                      "requirement"
                    ],
                    "additionalProperties": false
                  }
                },
                "required": [
                  "input"
                ],
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The capability's own result envelope plus `requestId`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "400": {
            "description": "`VALIDATION_ERROR` (input failed the capability's schema) or `INVALID_JSON` (unparseable request body).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid, expired, or revoked bearer token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthErrorEnvelope"
                }
              }
            }
          },
          "403": {
            "description": "`SCOPE_DENIED` — the token lacks a required scope, or the tenant is below `x-askcv-min-tier`. Emitted by the route with the standard `ErrorEnvelope`; the shared agent-auth ENTRY gate emits the leaner `AuthErrorEnvelope` for a wrong-credential-type token, so both shapes are documented.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/ErrorEnvelope"
                    },
                    {
                      "$ref": "#/components/schemas/AuthErrorEnvelope"
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "description": "`NOT_FOUND` (unknown or non-exported capability) or `FEATURE_UNAVAILABLE` (currently launch-hidden).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "422": {
            "description": "`TOOL_FAILED` — the capability ran and reported a failure.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorEnvelope"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded — either the shared per-tenant surface budget or, when this operation declares `x-askcv-rate-limit`, its own tighter per-capability budget (charged on the same key over every transport). The budget really was spent, so `retryAfterMs` is an honest hint. Body: `{ error, retryAfterMs }`.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        },
        "x-askcv-side": "read",
        "x-askcv-min-tier": "free",
        "x-askcv-cost-class": "standard",
        "x-askcv-required-scopes": [
          "tool:search_evidence",
          "agent:query"
        ],
        "x-askcv-coarse-scopes": [
          [
            "agent:query"
          ]
        ],
        "x-askcv-aliases": [
          "evidence.search"
        ]
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "An AskCV **agent** API token (`api_tokens.token_type = \"agent\"`), created under Settings → API Keys. This surface accepts NO other credential: `requireAgentAuthAnyScope` rejects every other token type, including MCP tokens, which belong to the separate `/api/mcp` surface. Coarse `agent:*` scopes and explicit per-capability `tool:<name>` grants are both honoured — see `x-askcv-required-scopes` and `x-askcv-coarse-scopes` per operation."
      }
    },
    "schemas": {
      "ErrorEnvelope": {
        "type": "object",
        "properties": {
          "code": {
            "type": "string"
          },
          "message": {
            "type": "string"
          },
          "retryable": {
            "type": "boolean"
          },
          "requestId": {
            "type": "string"
          }
        },
        "required": [
          "code",
          "message",
          "retryable",
          "requestId"
        ]
      },
      "AuthErrorEnvelope": {
        "type": "object",
        "properties": {
          "error": {
            "type": "string"
          }
        },
        "required": [
          "error"
        ],
        "description": "Emitted by the shared agent-auth entry gate. Deliberately leaner than `ErrorEnvelope` (no `requestId`) — see `authErrorResponse` in scripts/generate-openapi-core.ts for why this asymmetry is documented rather than normalized away."
      },
      "CapabilityListEntry": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "side": {
            "type": "string",
            "enum": [
              "read",
              "write"
            ]
          },
          "minTier": {
            "type": "string"
          },
          "aliases": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Dotted names this capability also answers to. Additive; the canonical `name` remains the single catalog entry."
          },
          "inputSchema": {
            "type": "object"
          }
        },
        "required": [
          "name",
          "description",
          "side",
          "minTier",
          "aliases",
          "inputSchema"
        ]
      }
    }
  }
}
