{
  "openapi": "3.1.0",
  "info": {
    "title": "akp-server",
    "description": "HTTP and streaming service exposing AKP domain operations.",
    "contact": {
      "name": "Simon Johnson"
    },
    "license": {
      "name": "Apache-2.0",
      "identifier": "Apache-2.0"
    },
    "version": "0.1.0"
  },
  "paths": {
    "/health": {
      "get": {
        "tags": [],
        "summary": "Liveness. Never authenticated, never gated.",
        "operationId": "health",
        "responses": {
          "200": {
            "description": "The service is up"
          }
        }
      }
    },
    "/v1/account/billing-address": {
      "get": {
        "tags": [],
        "summary": "This account's billing address.",
        "description": "Any member may read it. Only an owner may change it — the same split the rest of the account's\nadministration uses, and for the same reason: knowing where your employer's invoices go is not\na privilege, and changing it is.",
        "operationId": "get_billing_address",
        "responses": {
          "200": {
            "description": "Where invoices go",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BillingAddress"
                }
              }
            }
          },
          "404": {
            "description": "This deployment keeps no accounts"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ]
      },
      "put": {
        "tags": [],
        "summary": "Replace it.",
        "operationId": "set_billing_address",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BillingAddress"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "The address as it now stands",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BillingAddress"
                }
              }
            }
          },
          "400": {
            "description": "A field is too long, or the country is not two letters"
          },
          "403": {
            "description": "Not an owner"
          },
          "404": {
            "description": "This deployment keeps no accounts"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ]
      }
    },
    "/v1/account/entitlement": {
      "get": {
        "tags": [],
        "summary": "What this account's plan permits.",
        "description": "Its own route rather than a field on the capability payload, because it is an answer about an\n*account* and capabilities answer about a *session* — and because a screen that shows a trial\ncountdown wants to refetch it without re-asking what the whole session may do.",
        "operationId": "account_entitlement",
        "responses": {
          "200": {
            "description": "The plan, the trial end date, and what is permitted"
          },
          "401": {
            "description": "No session"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ]
      }
    },
    "/v1/account/keys": {
      "get": {
        "tags": [],
        "summary": "Every key this account holds (FR-1041).",
        "operationId": "list_api_keys",
        "responses": {
          "200": {
            "description": "The keys this account holds, newest first",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/ApiKey"
                  }
                }
              }
            }
          },
          "403": {
            "description": "Not an owner"
          },
          "404": {
            "description": "No such endpoint for this deployment"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ]
      },
      "post": {
        "tags": [],
        "summary": "Mint a key, returning its secret exactly once (FR-1040).",
        "operationId": "issue_api_key",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/NewApiKey"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "The key, and the only copy of its secret",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/IssuedKey"
                }
              }
            }
          },
          "400": {
            "description": "A key needs a name"
          },
          "403": {
            "description": "Not an owner, or the key would outrank its issuer"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ]
      }
    },
    "/v1/account/keys/{id}": {
      "delete": {
        "tags": [],
        "summary": "Turn a key off (FR-1042).",
        "operationId": "revoke_api_key",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Revoked"
          },
          "403": {
            "description": "Not an owner"
          },
          "404": {
            "description": "No such live key"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ]
      }
    },
    "/v1/account/members": {
      "get": {
        "tags": [],
        "summary": "Everyone in the account.",
        "description": "Readable by any member: knowing who your colleagues are is not an administrative privilege,\nand a list you cannot see is how people end up mailing an owner to ask who to ask.",
        "operationId": "list_members",
        "responses": {
          "200": {
            "description": "Members, longest-standing first",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Member"
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": []
          }
        ]
      },
      "post": {
        "tags": [],
        "summary": "Add somebody to the account.",
        "operationId": "add_member",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/NewMember"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "The new member",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Member"
                }
              }
            }
          },
          "400": {
            "description": "No user with that email address"
          },
          "403": {
            "description": "Not an owner"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ]
      }
    },
    "/v1/account/members/{user_id}": {
      "delete": {
        "tags": [],
        "summary": "Remove somebody from the account.",
        "operationId": "remove_member",
        "parameters": [
          {
            "name": "user_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Removed"
          },
          "400": {
            "description": "This would leave the account with no owner"
          },
          "403": {
            "description": "Not an owner"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ]
      },
      "patch": {
        "tags": [],
        "summary": "Change what a member may do (FR-1031).",
        "operationId": "set_member_role",
        "parameters": [
          {
            "name": "user_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RoleChange"
              }
            }
          },
          "required": true
        },
        "responses": {
          "204": {
            "description": "Changed"
          },
          "400": {
            "description": "This would leave the account with no owner"
          },
          "403": {
            "description": "Not an owner"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ]
      }
    },
    "/v1/account/profile": {
      "get": {
        "tags": [],
        "summary": "Your own contact details and marketing preference.",
        "operationId": "get_profile",
        "responses": {
          "200": {
            "description": "Your details",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Profile"
                }
              }
            }
          },
          "404": {
            "description": "This deployment keeps no profiles"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ]
      },
      "patch": {
        "tags": [],
        "summary": "Change what you may change about yourself.",
        "operationId": "update_profile",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ProfileUpdate"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Your details, as they now stand",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Profile"
                }
              }
            }
          },
          "400": {
            "description": "A field is too long, or a display name was blanked"
          },
          "404": {
            "description": "This deployment keeps no profiles"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ]
      }
    },
    "/v1/account/profile/requests": {
      "post": {
        "tags": [],
        "summary": "Ask for one of the fixed fields to be changed.",
        "operationId": "request_profile_change",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ChangeRequestInput"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "Recorded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ChangeRequest"
                }
              }
            }
          },
          "400": {
            "description": "Nothing asked for, or too long"
          },
          "404": {
            "description": "This deployment keeps no profiles"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ]
      }
    },
    "/v1/account/profile/requests/{id}": {
      "delete": {
        "tags": [],
        "summary": "Withdraw one you asked for.",
        "operationId": "withdraw_profile_change",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "Request identifier",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Withdrawn"
          },
          "404": {
            "description": "You have no open request with that identifier"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ]
      }
    },
    "/v1/account/subscription": {
      "get": {
        "tags": [
          "crate::routes_billing"
        ],
        "summary": "This account's subscription, payments and history.",
        "operationId": "subscription",
        "responses": {
          "200": {
            "description": "The subscription, or the plan without one"
          },
          "404": {
            "description": "This deployment has no billing"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ]
      }
    },
    "/v1/account/subscription/cancel": {
      "post": {
        "tags": [
          "crate::routes_billing"
        ],
        "summary": "Cancel this account's subscription.",
        "operationId": "cancel",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CancelRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "204": {
            "description": "Cancelled, or scheduled to cancel"
          },
          "400": {
            "description": "Nothing to cancel"
          },
          "403": {
            "description": "Owner only"
          },
          "404": {
            "description": "This deployment has no billing"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ]
      }
    },
    "/v1/account/subscription/checkout": {
      "post": {
        "tags": [
          "crate::routes_billing"
        ],
        "summary": "Start a hosted checkout.",
        "operationId": "checkout",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CheckoutRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Where to send the browser"
          },
          "400": {
            "description": "That plan cannot be bought"
          },
          "403": {
            "description": "Owner only"
          },
          "404": {
            "description": "This deployment has no billing"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ]
      }
    },
    "/v1/account/subscription/resume": {
      "post": {
        "tags": [
          "crate::routes_billing"
        ],
        "summary": "Undo a scheduled cancellation.",
        "operationId": "resume",
        "responses": {
          "204": {
            "description": "The subscription will renew after all"
          },
          "400": {
            "description": "Nothing to resume"
          },
          "403": {
            "description": "Owner only"
          },
          "404": {
            "description": "This deployment has no billing"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ]
      }
    },
    "/v1/admin/accounts/{account}": {
      "get": {
        "tags": [],
        "summary": "One account, in full.",
        "operationId": "admin_account_detail",
        "parameters": [
          {
            "name": "account",
            "in": "path",
            "description": "Account slug",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Members, bundles, subscription and payments"
          },
          "404": {
            "description": "Not an operator, or no such account"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ]
      }
    },
    "/v1/admin/accounts/{account}/billing": {
      "get": {
        "tags": [
          "crate::routes_billing"
        ],
        "summary": "One account's billing, in full.",
        "operationId": "admin_account",
        "parameters": [
          {
            "name": "account",
            "in": "path",
            "description": "Account slug",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Subscription, payments and history"
          },
          "404": {
            "description": "Not an operator"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ]
      }
    },
    "/v1/admin/accounts/{account}/emails": {
      "get": {
        "tags": [],
        "summary": "What this account has been sent.",
        "description": "Operator-only, and it carries no message bodies — see `migrations/0038`. The question this\nanswers is \"did they get it, and did it leave\", which the subject and the outcome settle.",
        "operationId": "admin_account_emails",
        "parameters": [
          {
            "name": "account",
            "in": "path",
            "description": "Account slug",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Messages sent about this account, newest first"
          },
          "404": {
            "description": "Not an operator"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ]
      }
    },
    "/v1/admin/accounts/{account}/erase": {
      "post": {
        "tags": [],
        "operationId": "admin_erase_account",
        "parameters": [
          {
            "name": "account",
            "in": "path",
            "description": "Account slug",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EraseRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "204": {
            "description": "The account was erased"
          },
          "400": {
            "description": "The confirmation did not match"
          },
          "404": {
            "description": "Not an operator, or no such account"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ]
      }
    },
    "/v1/admin/accounts/{account}/payments/{payment}/refund": {
      "post": {
        "tags": [
          "crate::routes_billing"
        ],
        "summary": "Give money back.",
        "operationId": "admin_refund",
        "parameters": [
          {
            "name": "account",
            "in": "path",
            "description": "Account slug",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "payment",
            "in": "path",
            "description": "Payment identifier",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RefundRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "The payment as it now stands"
          },
          "400": {
            "description": "More than is left to refund"
          },
          "404": {
            "description": "Not an operator, or no such payment"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ]
      }
    },
    "/v1/admin/accounts/{account}/resend-welcome": {
      "post": {
        "tags": [],
        "summary": "Send the welcome message again, to the address on the account.",
        "description": "The welcome ordinarily fires when somebody redeems a verification link, which means an account\ncreated while `AKP_SKIP_EMAIL_VERIFICATION` was set never produced one and never will — there\nis no redemption left to trigger it. That is not a hypothetical: every account registered\nbefore mail was configured is in exactly that state.\n\nTakes no address. It goes to whoever the account says its owner is, so an operator cannot use\nthis to mail an arbitrary recipient from our domain.",
        "operationId": "admin_resend_welcome",
        "parameters": [
          {
            "name": "account",
            "in": "path",
            "description": "Account slug",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Sent, with the address it went to"
          },
          "400": {
            "description": "Nobody on this account has an address"
          },
          "404": {
            "description": "Not an operator, or no such account"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ]
      }
    },
    "/v1/admin/accounts/{account}/standing": {
      "post": {
        "tags": [],
        "operationId": "admin_set_account_standing",
        "parameters": [
          {
            "name": "account",
            "in": "path",
            "description": "Account slug",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/StandingRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "204": {
            "description": "The standing was set"
          },
          "404": {
            "description": "Not an operator, or no such account"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ]
      }
    },
    "/v1/admin/accounts/{account}/subscription/cancel": {
      "post": {
        "tags": [
          "crate::routes_billing"
        ],
        "summary": "Cancel on a customer's behalf.",
        "operationId": "admin_cancel",
        "parameters": [
          {
            "name": "account",
            "in": "path",
            "description": "Account slug",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CancelRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "204": {
            "description": "Cancelled"
          },
          "400": {
            "description": "Nothing to cancel"
          },
          "404": {
            "description": "Not an operator"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ]
      }
    },
    "/v1/admin/accounts/{account}/subscription/grant": {
      "post": {
        "tags": [
          "crate::routes_billing"
        ],
        "summary": "Put an account on a plan without a provider behind it.",
        "operationId": "admin_grant",
        "parameters": [
          {
            "name": "account",
            "in": "path",
            "description": "Account slug",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/GrantRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "204": {
            "description": "Granted"
          },
          "404": {
            "description": "Not an operator"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ]
      }
    },
    "/v1/admin/aliases/{alias}": {
      "put": {
        "tags": [],
        "summary": "Assign an alias, or point an existing one somewhere else.",
        "operationId": "admin_set_alias",
        "parameters": [
          {
            "name": "alias",
            "in": "path",
            "description": "The short name",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AliasInput"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "The alias as it now stands",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Alias"
                }
              }
            }
          },
          "400": {
            "description": "Not an alias-shaped name, or not a package identity"
          },
          "404": {
            "description": "Not an operator, or no such package"
          },
          "409": {
            "description": "That package already has an alias"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ]
      },
      "delete": {
        "tags": [],
        "summary": "Withdraw one.",
        "operationId": "admin_remove_alias",
        "parameters": [
          {
            "name": "alias",
            "in": "path",
            "description": "The short name",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Withdrawn"
          },
          "404": {
            "description": "Not an operator, or no such alias"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ]
      }
    },
    "/v1/admin/appearance": {
      "put": {
        "tags": [],
        "summary": "Set it, for everybody who has not chosen otherwise.",
        "operationId": "admin_set_appearance",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Appearance"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "The house style as it now stands",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Appearance"
                }
              }
            }
          },
          "400": {
            "description": "A theme that is neither light nor dark"
          },
          "404": {
            "description": "Not an operator"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ]
      }
    },
    "/v1/admin/funnel": {
      "get": {
        "tags": [],
        "summary": "Everybody in the funnel who has not converted.",
        "operationId": "admin_funnel",
        "parameters": [
          {
            "name": "closed",
            "in": "query",
            "description": "Include processed and not-interested",
            "required": false,
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The queue, oldest first"
          },
          "404": {
            "description": "Not an operator"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ]
      }
    },
    "/v1/admin/funnel/campaigns": {
      "get": {
        "tags": [],
        "summary": "How many arrived from each campaign.",
        "operationId": "admin_campaign_report",
        "responses": {
          "200": {
            "description": "Counts by campaign and kind"
          },
          "404": {
            "description": "Not an operator"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ]
      }
    },
    "/v1/admin/funnel/rescore": {
      "post": {
        "tags": [],
        "summary": "Recompute every derived lead score.",
        "description": "A deliberate act with a visible result rather than a background job, because the number moving\nunder somebody working through a list is worse than the number being a day old.",
        "operationId": "admin_rescore_funnel",
        "responses": {
          "200": {
            "description": "How many entries were rescored"
          },
          "404": {
            "description": "Not an operator"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ]
      }
    },
    "/v1/admin/funnel/{id}": {
      "patch": {
        "tags": [],
        "summary": "Move an entry along the pipeline.",
        "operationId": "admin_set_funnel_status",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "Funnel entry identifier",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/FunnelStatusChange"
              }
            }
          },
          "required": true
        },
        "responses": {
          "204": {
            "description": "Moved"
          },
          "400": {
            "description": "Unknown status"
          },
          "404": {
            "description": "Not an operator, or no such entry"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ]
      }
    },
    "/v1/admin/funnel/{id}/assessment": {
      "patch": {
        "tags": [],
        "summary": "Record a judgement a person made about a lead.",
        "operationId": "admin_set_assessment",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "Funnel entry identifier",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ManualAssessment"
              }
            }
          },
          "required": true
        },
        "responses": {
          "204": {
            "description": "Recorded"
          },
          "400": {
            "description": "A score outside 0 to 10"
          },
          "404": {
            "description": "Not an operator, or no such entry"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ]
      }
    },
    "/v1/admin/funnel/{id}/enrichment": {
      "delete": {
        "tags": [],
        "summary": "Never profile this lead, and forget what was inferred about them.",
        "description": "The subject-rights control. Somebody who objects to being profiled says so, and this is what\nhonours it: the flag stops the worker ever claiming the row again, and the inferred company\nname and notes are cleared. Their own words — what they typed on the form — are left, because\nthose they gave us.\n\nA `DELETE` on the enrichment rather than on the entry: deleting the funnel row would lose the\nrecord that they asked, and a later import would enrich them again.",
        "operationId": "admin_opt_out_of_enrichment",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "Funnel entry identifier",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Never again, and what was inferred is gone"
          },
          "404": {
            "description": "Not an operator, or no such entry"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ]
      }
    },
    "/v1/admin/namespaces": {
      "get": {
        "tags": [],
        "summary": "Every namespace claim, across every account.",
        "operationId": "admin_namespaces",
        "responses": {
          "200": {
            "description": "Every claim, verified or not"
          },
          "404": {
            "description": "Not an operator"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ]
      }
    },
    "/v1/admin/namespaces/{namespace}": {
      "delete": {
        "tags": [],
        "summary": "Revoke a namespace claim.",
        "description": "The operator counterpart to the DNS proof: a claim that was verified and should not have been\n— a domain that changed hands, a challenge record left in place by a previous owner — is\nremoved here rather than waiting for a re-check that may never disagree.",
        "operationId": "admin_revoke_namespace",
        "parameters": [
          {
            "name": "namespace",
            "in": "path",
            "description": "The namespace to revoke",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Revoked"
          },
          "404": {
            "description": "Not an operator, or no such claim"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ]
      }
    },
    "/v1/admin/plans": {
      "get": {
        "tags": [
          "crate::routes_billing"
        ],
        "summary": "Every plan, with the provider identifiers.",
        "operationId": "admin_plans",
        "responses": {
          "200": {
            "description": "The price list, as staff see it"
          },
          "404": {
            "description": "Not an operator"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ]
      }
    },
    "/v1/admin/plans/{code}": {
      "patch": {
        "tags": [
          "crate::routes_billing"
        ],
        "summary": "Change what a plan is called, costs, and charges against.",
        "operationId": "admin_update_plan",
        "parameters": [
          {
            "name": "code",
            "in": "path",
            "description": "Plan code",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PlanUpdate"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "The plan as it now stands"
          },
          "400": {
            "description": "Unusable values"
          },
          "404": {
            "description": "Not an operator, or no such plan"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ]
      }
    },
    "/v1/admin/profile-requests": {
      "get": {
        "tags": [],
        "summary": "Everything waiting for staff to action.",
        "operationId": "admin_profile_requests",
        "responses": {
          "200": {
            "description": "Open requests, oldest first",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/PendingChange"
                  }
                }
              }
            }
          },
          "404": {
            "description": "Not an operator"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ]
      }
    },
    "/v1/admin/profile-requests/{id}": {
      "patch": {
        "tags": [],
        "operationId": "admin_settle_profile_request",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "Request identifier",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SettleRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "204": {
            "description": "Settled"
          },
          "400": {
            "description": "Asked to reopen it"
          },
          "404": {
            "description": "Not an operator, or already settled"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ]
      }
    },
    "/v1/admin/subscriptions": {
      "get": {
        "tags": [
          "crate::routes_billing"
        ],
        "summary": "Every subscription, across every tenancy.",
        "operationId": "admin_subscriptions",
        "responses": {
          "200": {
            "description": "The directory, and recurring revenue"
          },
          "404": {
            "description": "Not an operator"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ]
      }
    },
    "/v1/agent/turn": {
      "post": {
        "tags": [],
        "summary": "Advance an agent conversation (FR-843, FR-844, FR-848, FR-849, ADR-0036).",
        "description": "Capability-gated, and **not** authenticated for the discovery surface. A visitor may ask\nabout the public registry; the credential they did not present is what scopes their tools\nto it. Where the caller has no AI capability at all this is a `404`, indistinguishable from\nan endpoint that was never deployed.",
        "operationId": "agent_turn",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TurnRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "The agent's reply and any proposals"
          },
          "400": {
            "description": "No message, or an authoring turn with no project"
          },
          "404": {
            "description": "No such endpoint for this caller"
          },
          "429": {
            "description": "Account budget or rate limit reached"
          },
          "502": {
            "description": "The agent service is unavailable"
          }
        }
      }
    },
    "/v1/aliases": {
      "get": {
        "tags": [],
        "summary": "Every alias this registry keeps.",
        "description": "Unauthenticated, like the rest of the read surface. A row names a package and nothing more;\nwhether anything under it can be read is decided when it is resolved.",
        "operationId": "list_aliases",
        "responses": {
          "200": {
            "description": "Aliases, alphabetically",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Alias"
                  }
                }
              }
            }
          },
          "404": {
            "description": "This deployment keeps no aliases"
          }
        }
      }
    },
    "/v1/aliases/{alias}": {
      "get": {
        "tags": [],
        "summary": "What an alias names, and the newest version of it you may see.",
        "description": "The call `akp deploy mqtt` makes. Unauthenticated, because pulling a public package needs no\naccount (invariant 11) — and a session, where there is one, is what makes an alias pointing at\nthat account's private package resolve at all.",
        "operationId": "resolve_alias",
        "parameters": [
          {
            "name": "alias",
            "in": "path",
            "description": "The short name",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The coordinate it stands for",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Resolved"
                }
              }
            }
          },
          "400": {
            "description": "Not an alias-shaped name"
          },
          "404": {
            "description": "No such alias, or nothing under it you may see"
          }
        }
      }
    },
    "/v1/appearance": {
      "get": {
        "tags": [],
        "summary": "What the site looks like by default.",
        "description": "Unauthenticated, like the rest of the read surface, and deliberately so: the front door is what\nmost needs it, and a visitor who has to sign in before the site is the right colour has been\nshown the wrong one first.",
        "operationId": "get_appearance",
        "responses": {
          "200": {
            "description": "The house style, where one is set",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Appearance"
                }
              }
            }
          },
          "404": {
            "description": "This deployment keeps no settings"
          }
        }
      }
    },
    "/v1/assist/voice": {
      "post": {
        "tags": [],
        "summary": "Speak a reply, through this deployment's voice (ADR-0047).",
        "description": "## Why the server does this at all\n\nThe provider bills per character against a key that must never reach a browser. So this is a\nproxy, and it exists only where a key is configured — a deployment without one answers 404\nand the client uses the browser's own synthesiser, which is what everything did before.\n\n## Rate limited as a turn is\n\nIt costs money per press and answers unauthenticated visitors, which is the combination that\nfunds somebody else's audiobook. The same limiter the conversation uses, keyed the same way.",
        "operationId": "speak",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SpeakRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Audio, as MPEG",
            "content": {
              "audio/mpeg": {}
            }
          },
          "404": {
            "description": "This deployment has no hosted voice"
          },
          "429": {
            "description": "Too many requests"
          }
        }
      }
    },
    "/v1/auth/password": {
      "post": {
        "tags": [],
        "summary": "Sign in with an email address and a password (ADR-0037).",
        "operationId": "auth_password",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PasswordRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "A session, shown once"
          },
          "401": {
            "description": "That address and password do not match an account"
          },
          "404": {
            "description": "This deployment has no sign-in"
          }
        }
      }
    },
    "/v1/auth/password/reset": {
      "post": {
        "tags": [],
        "summary": "Ask for a reset link.",
        "description": "Always `204`, for an address that exists and one that does not. Same reasoning as\nregistration, and here the oracle would be even cheaper to operate: no password to guess and\nno rate limit that a list of a thousand addresses would trip in a way a single one would not.",
        "operationId": "auth_reset_request",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EmailRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "204": {
            "description": "Acknowledged, whether or not the address is known"
          },
          "404": {
            "description": "This deployment has no sign-in"
          }
        }
      }
    },
    "/v1/auth/password/reset/complete": {
      "post": {
        "tags": [],
        "summary": "Redeem a reset link and set a new password.",
        "operationId": "auth_reset_complete",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ResetCompleteRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "204": {
            "description": "The password is changed and every session is revoked"
          },
          "400": {
            "description": "The password is too weak"
          },
          "401": {
            "description": "The link is unknown, expired or already used"
          },
          "404": {
            "description": "This deployment has no sign-in"
          }
        }
      }
    },
    "/v1/auth/providers": {
      "get": {
        "tags": [],
        "summary": "The providers this deployment offers.",
        "description": "An empty list is a supported answer and the default. A deployment with no identity provider\nhas no sign-in at all, and a client that receives nothing renders nothing — rather than a\nbutton that fails when pressed. Same rule as the AI surface: absent, not disabled.",
        "operationId": "auth_providers",
        "responses": {
          "200": {
            "description": "Identity providers, possibly none"
          }
        }
      }
    },
    "/v1/auth/register": {
      "post": {
        "tags": [],
        "summary": "Register with an email address and a password (ADR-0037, FR-688).",
        "description": "Answers `200` whether or not the address was free.\n\nThat is the uncomfortable part and it is deliberate. A `409` here is an oracle: point it at a\nlist of addresses and it reports which of them hold accounts, which for a registry of\ncommercial knowledge packages is a list of which of your competitors are customers. So a held\naddress gets the same shape of answer as a free one — but with no token, because there is no\nsession to give — and a mail goes to the address saying somebody tried to register it and\noffering the reset flow. The person who actually owns the address learns what happened,\nthrough the channel that proves they own it; a prober learns nothing.",
        "operationId": "auth_register",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RegisterRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "A session, or an acknowledgement that reveals nothing"
          },
          "400": {
            "description": "The address or password is unusable"
          },
          "404": {
            "description": "This deployment has no sign-in"
          }
        }
      }
    },
    "/v1/auth/verify": {
      "post": {
        "tags": [],
        "summary": "Prove an address (FR-688).",
        "description": "`POST` rather than `GET`, and the link in the mail points at a page in the application that\nposts this when a button is pressed. Two reasons, and the second is the load-bearing one:\na `GET` that changes state is the wrong verb, and mail security scanners follow links in\nmessages. A single-use token consumed by a scanner is a sign-up that fails for a reason\nnobody can diagnose, and it fails disproportionately for corporate recipients — the exact\npopulation worth having.",
        "operationId": "auth_verify",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/VerifyRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "204": {
            "description": "The address is proved"
          },
          "400": {
            "description": "The link is unknown, expired or already used"
          },
          "404": {
            "description": "This deployment has no sign-in"
          }
        }
      }
    },
    "/v1/auth/verify/resend": {
      "post": {
        "tags": [],
        "summary": "Send the confirmation again, to the address on record.",
        "description": "Authenticated, and it takes no address: the address is whatever the session's user currently\nholds. An unauthenticated resend that accepted an address would be an open relay pointed at\nanybody, with our sending reputation paying for it.",
        "operationId": "auth_resend_verification",
        "responses": {
          "204": {
            "description": "Sent, or already verified"
          },
          "401": {
            "description": "No session"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ]
      }
    },
    "/v1/auth/{provider}/callback": {
      "get": {
        "tags": [],
        "summary": "Complete a sign-in and issue a session.",
        "description": "The response carries the token in a JSON body rather than a cookie or a URL fragment.\nA fragment is the pattern OAuth 2.1 moved away from, and a cookie would need CSRF\nprotection and a decision about `SameSite` that no front end is yet asking for. A body is the\nsmallest honest thing that works today; a browser handoff arrives with the front end that\nneeds one.",
        "operationId": "auth_callback",
        "parameters": [
          {
            "name": "provider",
            "in": "path",
            "description": "Provider identifier",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A session, shown once"
          },
          "400": {
            "description": "The provider refused, or the callback is unusable"
          },
          "404": {
            "description": "No such provider is configured"
          }
        }
      }
    },
    "/v1/auth/{provider}/start": {
      "get": {
        "tags": [],
        "operationId": "auth_start",
        "parameters": [
          {
            "name": "provider",
            "in": "path",
            "description": "Provider identifier",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "signup",
            "in": "query",
            "description": "Open the registration form instead",
            "required": false,
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Where to send the browser"
          },
          "404": {
            "description": "No such provider is configured"
          }
        }
      }
    },
    "/v1/billing/plans": {
      "get": {
        "tags": [
          "crate::routes_billing"
        ],
        "summary": "The price list.",
        "operationId": "plans",
        "responses": {
          "200": {
            "description": "Every plan on offer"
          },
          "404": {
            "description": "This deployment has no billing"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ]
      }
    },
    "/v1/billing/webhook": {
      "post": {
        "tags": [
          "crate::routes_billing"
        ],
        "summary": "What the provider says happened.",
        "description": "Takes the raw body before anything parses it, because the signature covers those exact bytes —\na body that has been through `serde_json` and back would not verify.\n\nAnswers `200` to anything it has verified, including events it does nothing about. A non-2xx\nmakes the provider retry, so refusing an event nobody wanted produces an escalating retry\nstorm for something that was never a problem. The only `4xx` here is a signature that did not\ncheck out, which should never be retried.",
        "operationId": "webhook",
        "requestBody": {
          "description": "The provider's event, verbatim. Read as raw bytes because the signature covers exactly these — a body reserialised from a parsed form would not verify.",
          "content": {
            "text/plain": {
              "schema": {
                "type": "string"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Accepted, whether or not it was acted on"
          },
          "400": {
            "description": "Not signed by the payment provider"
          },
          "404": {
            "description": "This deployment has no billing"
          }
        }
      }
    },
    "/v1/deployments": {
      "get": {
        "tags": [],
        "summary": "Everything this account is running.",
        "operationId": "list_deployments",
        "responses": {
          "200": {
            "description": "This account's deployments, newest first",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Deployment"
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": []
          }
        ]
      },
      "post": {
        "tags": [],
        "summary": "Host a bundle: create a deployment and seed its runtime (PRD-0012, ADR-0051).",
        "description": "## Preparation happens here, before the response\n\nSynchronous on purpose for now. A deployment that returns `preparing` and becomes ready\nlater needs a worker, a queue and a way for a client to find out — and all three would be\nbuilt before anybody had seen a single deployment answer a question. Seeding is bounded by\nthe size of the bundle, the status model already carries `preparing` for when this moves off\nthe request path, and nothing about the stored shape has to change when it does.\n\n## Composition is the same composition the CLI does\n\nThe bound release, plus everything it requires, composed through `akp_adapter::compose`\nexactly as `akp deploy` composes it (ADR-0050, FR-1061). A dependency this account cannot\nread is a dependency that does not exist to it, and preparation refuses rather than seeding a\nruntime that answers from part of a corpus.",
        "operationId": "create_deployment",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateDeploymentRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "Created and seeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Deployment"
                }
              }
            }
          },
          "400": {
            "description": "The request is unusable"
          },
          "404": {
            "description": "No such release, or this deployment has no registry"
          },
          "409": {
            "description": "A required bundle could not be supplied"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ]
      }
    },
    "/v1/deployments/{id}": {
      "get": {
        "tags": [],
        "summary": "One deployment.",
        "operationId": "get_deployment",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The deployment",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The deployment",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Deployment"
                }
              }
            }
          },
          "404": {
            "description": "No such deployment"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ]
      },
      "delete": {
        "tags": [],
        "summary": "Delete a deployment and everything it held (ADR-0051 §3).",
        "description": "The bundle and the registry are untouched. What goes is the runtime state and the request\nrecords, by cascade — so an account can say what was destroyed, and the answer is \"everything\nit had, and there was nowhere else for it to be\".",
        "operationId": "delete_deployment",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The deployment",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Deleted, with all of its state"
          },
          "404": {
            "description": "No such deployment"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ]
      }
    },
    "/v1/deployments/{id}/ask": {
      "post": {
        "tags": [],
        "summary": "Ask a deployment a question (FR-1066, FR-1067).",
        "description": "Answers from the runtime's own corpus and from nothing else. There is no registry read on\nthis path and no tool the runtime could reach: the isolation is what was built rather than\nwhat anything was told (ADR-0051).",
        "operationId": "ask_deployment",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The deployment",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AskRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "What it answered from",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AskResponse"
                }
              }
            }
          },
          "404": {
            "description": "No such deployment"
          },
          "409": {
            "description": "It is not running"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ]
      }
    },
    "/v1/deployments/{id}/pause": {
      "post": {
        "tags": [],
        "summary": "Stop a deployment. Its state is kept; resuming does not re-prepare (FR-1087).",
        "operationId": "pause_deployment",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The deployment",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Paused"
          },
          "404": {
            "description": "No such deployment"
          },
          "409": {
            "description": "It is not running"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ]
      }
    },
    "/v1/deployments/{id}/resume": {
      "post": {
        "tags": [],
        "summary": "Start a paused deployment again.",
        "operationId": "resume_deployment",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The deployment",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Running"
          },
          "404": {
            "description": "No such deployment"
          },
          "409": {
            "description": "It cannot be resumed"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ]
      }
    },
    "/v1/deployments/{id}/shares": {
      "get": {
        "tags": [],
        "summary": "Who this deployment has been given to.",
        "operationId": "list_deployment_shares",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The deployment",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Live shares, newest first",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Share"
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": []
          }
        ]
      },
      "post": {
        "tags": [],
        "summary": "Give this deployment to an address (FR-1075).",
        "description": "No token and no link with a secret in it. The share names an address and attaches when that\naddress signs in — so forwarding the message gains the recipient nothing, which is the\nproperty that matters when what is being handed over is a live endpoint over somebody's\nknowledge rather than a file.",
        "operationId": "share_deployment",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The deployment",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ShareDeploymentRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "Shared",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Share"
                }
              }
            }
          },
          "400": {
            "description": "The address or duration is unusable"
          },
          "404": {
            "description": "No such deployment"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ]
      }
    },
    "/v1/deployments/{id}/shares/{share}": {
      "delete": {
        "tags": [],
        "summary": "Withdraw a share.",
        "description": "Takes effect at once: the policy that widens a deployment to a recipient evaluates\n`revoked_at` on every read, so access stops when this commits rather than when something next\nsweeps.",
        "operationId": "revoke_deployment_share",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The deployment",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "share",
            "in": "path",
            "description": "The share",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Withdrawn"
          },
          "404": {
            "description": "No such share"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ]
      }
    },
    "/v1/documentation": {
      "get": {
        "tags": [],
        "summary": "The documentation, searched.",
        "description": "Unauthenticated: the specification and the tool are public, and an assistant that had to sign\na visitor in before it could say what an adapter is would be the feature gate invariant 13\nforbids, wearing a different hat.",
        "operationId": "documentation",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "description": "What to look for. Omitted returns the whole corpus.",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "topic",
            "in": "query",
            "description": "Narrow to `specification`, `tool` or `adapters`.",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "How many passages at most. Default 8.",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 0
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The passages most likely to answer",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Section"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/evaluation/runs/{run_id}": {
      "get": {
        "tags": [],
        "summary": "One evaluation run by identity.",
        "description": "**Always `404`.** Nothing records runs, so no identity resolves — see\n[`list_evaluation_runs`] and [`wire::EvaluationRun`]. A `404` is the honest answer to\n\"fetch the run with this identity\" when there are none, and it is the same answer the\nroute will give for a genuinely unknown identity once there are. Nothing about this\nhandler changes shape when a producer arrives; it gains a lookup.",
        "operationId": "get_evaluation_run",
        "parameters": [
          {
            "name": "run_id",
            "in": "path",
            "description": "Evaluation run identity",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The run",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EvaluationRun"
                }
              }
            }
          },
          "404": {
            "description": "No such run"
          }
        }
      }
    },
    "/v1/interest": {
      "post": {
        "tags": [],
        "summary": "Register interest in a hosted or self-hosted private registry.",
        "description": "Unauthenticated, because the person filling it in has no account and may never want one —\nwhich is the entire population this form exists to reach.\n\nAnswers `204` on success and says nothing else. There is nothing useful to return, and an\nendpoint that reported \"you have already registered interest\" would be an oracle over an\naddress list in exactly the way registration refuses to be.",
        "operationId": "register_interest",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/InterestRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "204": {
            "description": "Noted"
          },
          "400": {
            "description": "The address is unusable"
          },
          "404": {
            "description": "This deployment has no funnel"
          }
        }
      }
    },
    "/v1/namespaces": {
      "get": {
        "tags": [],
        "summary": "Every namespace this account has claimed.",
        "operationId": "list_namespaces",
        "responses": {
          "200": {
            "description": "This account's claims",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/NamespaceClaimView"
                  }
                }
              }
            }
          },
          "401": {
            "description": "No credential"
          },
          "404": {
            "description": "This deployment records no claims"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ]
      },
      "post": {
        "tags": [],
        "summary": "Claim a namespace (FR-1018, FR-1037).",
        "description": "Idempotent: asking again for a namespace this account already holds returns the same claim\nand the same token. Re-issuing would invalidate a record somebody has already placed, which\nis the worst possible answer to \"show me that page again\".",
        "operationId": "claim_namespace",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ClaimRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "Claimed; place the record and verify",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NamespaceClaimView"
                }
              }
            }
          },
          "400": {
            "description": "Not a reverse-DNS namespace, or a public suffix (FR-1039)"
          },
          "401": {
            "description": "No credential"
          },
          "403": {
            "description": "Another account holds an overlapping claim (FR-1033, FR-1038)"
          },
          "404": {
            "description": "This deployment records no claims"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ]
      }
    },
    "/v1/namespaces/{namespace}": {
      "delete": {
        "tags": [],
        "summary": "Give up a claim this account holds.",
        "description": "Owner-only, like claiming one. A claim somebody started and abandoned blocks the namespace for\neverybody else until its token expires, and there was no way to hand it back before the wait\nran out — so this is the missing half of `claim`, not a new capability.",
        "operationId": "withdraw_namespace",
        "parameters": [
          {
            "name": "namespace",
            "in": "path",
            "description": "The namespace to give up",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Withdrawn; the name is free again"
          },
          "401": {
            "description": "No credential"
          },
          "403": {
            "description": "Not an owner"
          },
          "404": {
            "description": "This account holds no such claim"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ]
      }
    },
    "/v1/namespaces/{namespace}/verify": {
      "post": {
        "tags": [],
        "summary": "Look for the challenge record now (FR-1037, FR-1045).",
        "description": "A `200` either way. \"Not visible yet\" is a successful check that found nothing, not a failed\nrequest, and returning it as a `4xx` would have a client render an error for the ordinary\ncase of asking thirty seconds after adding a record.",
        "operationId": "verify_namespace",
        "parameters": [
          {
            "name": "namespace",
            "in": "path",
            "description": "The claimed reverse-DNS namespace",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The check ran; `verified` says what it found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VerificationView"
                }
              }
            }
          },
          "401": {
            "description": "No credential"
          },
          "404": {
            "description": "No such claim on this account"
          },
          "502": {
            "description": "The lookup could not be completed"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ]
      }
    },
    "/v1/packages": {
      "get": {
        "tags": [],
        "summary": "Every release this caller may see, newest first (FR-310, FR-330).",
        "description": "One query, no blobs. Every field of every row comes from the listing projection recorded\nat publish time (migration 0006) — see that migration's header for why the alternative was\nunacceptable.\n\nNarrowed by [`SearchQuery`] where one is given. An unfiltered call is the front page; a\nfiltered one is the search box and the agent's search tool, and they are the same route\nbecause they are the same question with different arguments.",
        "operationId": "list_packages",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "description": "Free text over coordinate, name, summary, publisher and keywords.",
            "required": false,
            "schema": {
              "type": [
                "string",
                "null"
              ]
            }
          },
          {
            "name": "category",
            "in": "query",
            "description": "Catalogue category, matched exactly.",
            "required": false,
            "schema": {
              "type": [
                "string",
                "null"
              ]
            }
          },
          {
            "name": "industry",
            "in": "query",
            "description": "Industry, matched exactly against the declared list.",
            "required": false,
            "schema": {
              "type": [
                "string",
                "null"
              ]
            }
          },
          {
            "name": "archetype",
            "in": "query",
            "description": "Archetype, matched exactly.",
            "required": false,
            "schema": {
              "type": [
                "string",
                "null"
              ]
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "How many rows at most.",
            "required": false,
            "schema": {
              "type": [
                "integer",
                "null"
              ],
              "format": "int64"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Published releases, newest first",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/PackageSummary"
                  }
                }
              }
            }
          },
          "404": {
            "description": "This deployment has no registry"
          }
        }
      },
      "post": {
        "tags": [],
        "summary": "Publish an artifact into the caller's account (FR-1030, FR-1031).",
        "description": "The bytes are an `.akp` archive, posted as the request body rather than as a multipart form:\nthere is exactly one file and no other field, so a multipart envelope would be ceremony\naround a single value.\n\n# Visibility is stated, never defaulted\n\nFR-1003. A missing `visibility` is a `400`, not a public package. The failure mode this\nforbids is the one that cannot be walked back — a package becoming world-readable because a\nparameter was omitted, and nobody noticing until it is indexed.\n\n# Two preconditions, in the order they are cheapest to check\n\nThe account must be in good standing (FR-1040), and — for a public publish — must hold a\nverified claim on the namespace (FR-1002). Standing is asked first because it costs one\nin-memory answer and the archive has not been parsed yet; there is no sense decompressing\nfour gigabytes for an account that may not publish at all.\n\nNeither precondition touches a local operation. Building, validating, signing, verifying\nand deploying a package reach no server, which is the line invariant 13 draws and the\nreason this check is legitimate here and would not be in the CLI.",
        "operationId": "publish_package",
        "parameters": [
          {
            "name": "visibility",
            "in": "query",
            "description": "`public` or `private`. Required — see FR-1003.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "description": "An .akp archive",
          "content": {
            "application/octet-stream": {
              "schema": {
                "type": "array",
                "items": {
                  "type": "integer",
                  "format": "int32",
                  "minimum": 0
                }
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "Published",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PackageSummary"
                }
              }
            }
          },
          "400": {
            "description": "Visibility not stated, or the archive will not open"
          },
          "401": {
            "description": "No credential"
          },
          "403": {
            "description": "The account is not in good standing, or the namespace is unverified"
          },
          "409": {
            "description": "That coordinate is already published (FR-311)"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ]
      }
    },
    "/v1/packages/validate": {
      "post": {
        "tags": [],
        "summary": "Validate a package (FR-050…FR-055).",
        "description": "Unauthenticated. A non-conformant package is a `200` carrying a report that says so —\nsee [`crate::error`] on why that is not an error status.",
        "operationId": "validate_package",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ValidateRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "A conformance report, conformant or not"
          }
        }
      }
    },
    "/v1/packages/{id}/diff": {
      "get": {
        "tags": [],
        "summary": "Compare two releases of one package (FR-062).",
        "description": "Both operands are required and both must resolve. A version that is not published — which,\nfor a package with a single release, is any second version somebody names — is a `404`\nrather than a diff against nothing.\n\nComparing a release with itself is *not* an error. `akp_core::diff` answers it with an\nempty diff, which is the true answer to a true question, and inventing a failure for it\nwould mean this route disagreed with the CLI about the same comparison.",
        "operationId": "diff_package",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "Package identity",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "from",
            "in": "query",
            "description": "The earlier version",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "to",
            "in": "query",
            "description": "The later version",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The difference",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PackageDiff"
                }
              }
            }
          },
          "400": {
            "description": "Both from and to are required"
          },
          "404": {
            "description": "One of the versions is not published, or not yours"
          }
        }
      }
    },
    "/v1/packages/{id}/shares": {
      "get": {
        "tags": [],
        "summary": "Who this bundle has been shared with (FR-940, FR-941).",
        "operationId": "list_package_shares",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "Package identity",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Every share of this bundle, newest first",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Share"
                  }
                }
              }
            }
          }
        },
        "security": [
          {
            "bearer": []
          }
        ]
      },
      "post": {
        "tags": [],
        "operationId": "share_package",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "Package identity",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ShareRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "The share was created and the message sent",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ShareCreated"
                }
              }
            }
          },
          "400": {
            "description": "Not an address, or an expiry out of range"
          },
          "404": {
            "description": "No such bundle in this account"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ]
      }
    },
    "/v1/packages/{id}/shares/{share}": {
      "delete": {
        "tags": [],
        "summary": "Withdraw a share (FR-904, FR-942).",
        "operationId": "revoke_package_share",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "Package identity",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "share",
            "in": "path",
            "description": "Share identity",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Withdrawn"
          },
          "404": {
            "description": "No such share, or already withdrawn"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ]
      }
    },
    "/v1/packages/{id}/versions": {
      "get": {
        "tags": [],
        "summary": "Every version of one package, newest first (FR-063).",
        "description": "An unknown package and one this caller may not see both produce an empty result, and both\nbecome the same `404`. That is the point: a distinguishable answer here would let somebody\nenumerate private packages by coordinate.",
        "operationId": "list_versions",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "Package identity",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Versions, newest first",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/PackageVersionRef"
                  }
                }
              }
            }
          },
          "404": {
            "description": "No such package, or not yours"
          }
        }
      }
    },
    "/v1/packages/{id}/versions/{version}": {
      "get": {
        "tags": [],
        "summary": "One release in full (FR-320, FR-327).",
        "description": "The expensive route, and deliberately the only one that reassembles. Everything it returns\nis either a row, a descriptor from the release manifest, or something `akp-core` computed\nfrom the rebuilt package — and because reassembly verifies every blob against its\ndescriptor as it reads it, a package that reaches the response is one whose integrity has\nalready been established rather than asserted.\n\nDerived artifacts are *named* from the release manifest and never fetched. Deciding\nwhether a four-gigabyte embedding profile is worth transferring, before paying for it\nrather than after, is what FR-323 puts that metadata in the manifest for.",
        "operationId": "get_package",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "Package identity",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "version",
            "in": "path",
            "description": "Released version",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The release",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PackageDetail"
                }
              }
            }
          },
          "404": {
            "description": "No such release, or not yours"
          }
        }
      }
    },
    "/v1/packages/{id}/versions/{version}/archive": {
      "get": {
        "tags": [],
        "operationId": "get_archive",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "Package identity",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "version",
            "in": "path",
            "description": "Released version",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "profiles",
            "in": "query",
            "description": "`all`, or a comma-separated list of profile identifiers. Absent means canonical only.",
            "required": false,
            "schema": {
              "type": [
                "string",
                "null"
              ]
            }
          },
          {
            "name": "as",
            "in": "query",
            "description": "`zip` to name the download `.zip` instead of `.akp`.\n\n**The bytes are identical either way.** An AKP artifact *is* a ZIP container, and\ndetection is by byte signature rather than by filename (invariant 10, FR-233) — so\nrenaming it changes nothing a tool can observe. What it changes is whether a desktop\nrecognises it: macOS offers *Compress* on an unknown extension, which produces a zip of\nthe zip and then appears to contain nothing.\n\nA workaround for the operating system, in other words, not a second format. Anyone who\nwants to read the thing properly should use `akp inspect`, which identifies it by its\nsignature whatever it is called.",
            "required": false,
            "schema": {
              "type": [
                "string",
                "null"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The .akp artifact",
            "content": {
              "application/akp+zip": {}
            }
          },
          "404": {
            "description": "No such release, or not visible to this session"
          }
        }
      }
    },
    "/v1/packages/{id}/versions/{version}/evaluation/cases": {
      "get": {
        "tags": [],
        "summary": "The evaluation cases a release carries (FR-040, FR-042).",
        "description": "An empty array is a normal and conformant answer: an evaluation suite is optional, and a\npackage without one is not deficient.",
        "operationId": "list_evaluation_cases",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "Package identity",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "version",
            "in": "path",
            "description": "Released version",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Evaluation cases, possibly none",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/EvaluationCase"
                  }
                }
              }
            }
          },
          "404": {
            "description": "No such release, or not yours"
          }
        }
      }
    },
    "/v1/packages/{id}/versions/{version}/evaluation/runs": {
      "get": {
        "tags": [],
        "summary": "Evaluation runs recorded against a release (FR-080…FR-084).",
        "description": "**Always empty, and honestly so.** Nothing in this deployment executes an evaluation run:\nexecuting one means standing up a target — an adapter, a model, a retrieval stack — and\nthat is PRD-0007's continuous evaluation service, which does not exist yet. See\n[`wire::EvaluationRun`].\n\nThe release is still resolved first, so a request naming a version that is not published\ngets a `404` rather than an empty array. \"This release has no runs\" and \"there is no such\nrelease\" are different answers and a client acts differently on each.",
        "operationId": "list_evaluation_runs",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "Package identity",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "version",
            "in": "path",
            "description": "Released version",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Recorded runs; presently always none",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/EvaluationRun"
                  }
                }
              }
            }
          },
          "404": {
            "description": "No such release, or not yours"
          }
        }
      }
    },
    "/v1/packages/{id}/versions/{version}/knowledge-objects": {
      "get": {
        "tags": [],
        "summary": "The knowledge objects of one release (FR-012, FR-020).",
        "description": "The filters are the one thing on this surface that is genuinely a service concern: the AKP\nspecification defines an information model, not a query language, so there is no core\nfunction being duplicated here. Their exact rules are documented on\n[`wire::KnowledgeObjectQuery`], because a search whose behaviour is guessed at is a search\nwhose empty results cannot be explained.",
        "operationId": "list_knowledge_objects",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "Package identity",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "version",
            "in": "path",
            "description": "Released version",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "search",
            "in": "query",
            "description": "Case-insensitive substring over content and identity",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "classification",
            "in": "query",
            "description": "Classification the object must declare",
            "required": false,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Matching knowledge objects",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/KnowledgeObject"
                  }
                }
              }
            }
          },
          "404": {
            "description": "No such release, or not yours"
          }
        }
      }
    },
    "/v1/packages/{id}/versions/{version}/knowledge-objects/{ko_id}/provenance": {
      "get": {
        "tags": [],
        "summary": "Where one knowledge object came from (FR-011, FR-052).",
        "description": "The navigation PRD-0001 §24 requires: any derived chunk shown to a user leads back to the\nplace in the source it was taken from. Resolved by `akp_core::provenance`, which is also\nwhat the validator uses — so what this route shows and what conformance checks are the\nsame resolution rather than two that can drift apart.",
        "operationId": "get_provenance",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "Package identity",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "version",
            "in": "path",
            "description": "Released version",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "ko_id",
            "in": "path",
            "description": "Knowledge object identity",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The provenance trail",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Provenance"
                }
              }
            }
          },
          "404": {
            "description": "No such release or knowledge object"
          }
        }
      }
    },
    "/v1/packages/{id}/versions/{version}/manifest": {
      "get": {
        "tags": [],
        "summary": "The release manifest: the descriptor set, before any content (FR-320).",
        "operationId": "get_release_manifest",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "Package identity",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "version",
            "in": "path",
            "description": "Released version",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The descriptor set",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ReleaseManifestView"
                }
              }
            }
          },
          "404": {
            "description": "No such release, or not visible to this session"
          }
        }
      }
    },
    "/v1/packages/{id}/versions/{version}/provision": {
      "get": {
        "tags": [],
        "summary": "What a pull would cost, before paying for it (FR-323).",
        "operationId": "get_provision_plan",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "Package identity",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "version",
            "in": "path",
            "description": "Released version",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "profiles",
            "in": "query",
            "description": "`all`, or a comma-separated list of profile identifiers. Absent means canonical only.",
            "required": false,
            "schema": {
              "type": [
                "string",
                "null"
              ]
            }
          },
          {
            "name": "as",
            "in": "query",
            "description": "`zip` to name the download `.zip` instead of `.akp`.\n\n**The bytes are identical either way.** An AKP artifact *is* a ZIP container, and\ndetection is by byte signature rather than by filename (invariant 10, FR-233) — so\nrenaming it changes nothing a tool can observe. What it changes is whether a desktop\nrecognises it: macOS offers *Compress* on an unknown extension, which produces a zip of\nthe zip and then appears to contain nothing.\n\nA workaround for the operating system, in other words, not a second format. Anyone who\nwants to read the thing properly should use `akp inspect`, which identifies it by its\nsignature whatever it is called.",
            "required": false,
            "schema": {
              "type": [
                "string",
                "null"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "What this selection would transfer",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProvisionPlanView"
                }
              }
            }
          },
          "404": {
            "description": "No such release, or not visible to this session"
          }
        }
      }
    },
    "/v1/packages/{id}/versions/{version}/validation": {
      "get": {
        "tags": [],
        "summary": "Validate one release (FR-050…FR-055).",
        "description": "The rules are `akp_core::validate`'s and nothing here second-guesses them. Note the status\ncode: a non-conformant package is a `200` carrying a report that says so — see\n[`crate::error`] on why treating \"your package is invalid\" as a server error makes CI\nretry it.",
        "operationId": "validate_release",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "Package identity",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "version",
            "in": "path",
            "description": "Released version",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "A conformance report, conformant or not",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ValidationReport"
                }
              }
            }
          },
          "404": {
            "description": "No such release, or not yours"
          }
        }
      }
    },
    "/v1/session/capabilities": {
      "get": {
        "tags": [],
        "summary": "What this session can do (FR-800).",
        "description": "Answers for anonymous callers too, and its answer for them describes a product with no AI\nin it. This is the endpoint that lets the client contain no entitlement logic\n(ADR-0019 rule 4).",
        "operationId": "capabilities",
        "responses": {
          "200": {
            "description": "Capabilities for this session",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SessionCapabilities"
                }
              }
            }
          }
        }
      }
    },
    "/v1/shares/{token}": {
      "get": {
        "tags": [],
        "summary": "Spend a direct share (FR-910, FR-911).",
        "description": "Unauthenticated, because the mode exists so the bundle arrives with no account. The token is\nthe authority and it is spent by the statement that reads it, so two arrivals of the same URL\ncannot both succeed.",
        "operationId": "redeem_direct_share",
        "parameters": [
          {
            "name": "token",
            "in": "path",
            "description": "The one-time token",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The bundle"
          },
          "404": {
            "description": "Unknown, spent, withdrawn or lapsed"
          }
        }
      }
    },
    "/v1/support": {
      "post": {
        "tags": [],
        "summary": "Ask support a question (FR-688 is about proving an address; this is about answering one).",
        "description": "Authenticated, and that is what keeps it cheap: the sender is a known account, so there is no\ncaptcha, no rate limit and no moderation queue — the three things an open contact form needs\nand none of which is free. Somebody without an account uses the interest form, which is a\nsales conversation and reaches a different mailbox.\n\nAnswers `201` with the reference, because a person who has just described a problem wants\nsomething to quote back and an empty `204` gives them nothing to hold on to.",
        "operationId": "create_support_request",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SupportRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "Recorded, and acknowledged by email"
          },
          "400": {
            "description": "The subject or message is empty or too long"
          },
          "404": {
            "description": "This deployment has no support desk"
          }
        },
        "security": [
          {
            "bearer": []
          }
        ]
      }
    }
  },
  "components": {
    "schemas": {
      "AccountDetail": {
        "type": "object",
        "description": "Everything an operator sees about one account.",
        "required": [
          "account",
          "plan",
          "members",
          "bundles",
          "pendingInvitations",
          "payments",
          "billingHistory"
        ],
        "properties": {
          "account": {
            "type": "string",
            "description": "The slug."
          },
          "billingHistory": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/BillingEvent"
            },
            "description": "What has happened to their billing."
          },
          "bundles": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PublishedBundle"
            },
            "description": "What they have published."
          },
          "createdAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When the account was created."
          },
          "members": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Member"
            },
            "description": "Everybody on it, with their addresses and roles."
          },
          "name": {
            "type": [
              "string",
              "null"
            ],
            "description": "Display name, where the account has one."
          },
          "payments": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PaymentRecord"
            },
            "description": "What they have paid."
          },
          "pendingInvitations": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Invitation"
            },
            "description": "Invitations sent and not yet redeemed."
          },
          "plan": {
            "$ref": "#/components/schemas/Plan",
            "description": "What plan it is on."
          },
          "subscription": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/SubscriptionRecord",
                "description": "The live subscription, where there is one."
              }
            ]
          },
          "trialEndsAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When the free month ends, where one is running."
          }
        }
      },
      "AdminAccountBilling": {
        "type": "object",
        "description": "One account's billing, as staff see it.",
        "required": [
          "account",
          "payments",
          "history"
        ],
        "properties": {
          "account": {
            "type": "string",
            "description": "The account."
          },
          "history": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/BillingEvent"
            },
            "description": "Its history."
          },
          "payments": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PaymentRecord"
            },
            "description": "Its payments."
          },
          "subscription": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/SubscriptionRecord",
                "description": "Its subscription, where it has one."
              }
            ]
          }
        }
      },
      "AdminClaim": {
        "type": "object",
        "description": "One claim, as an operator sees it: with the account that holds it.",
        "required": [
          "namespace",
          "account",
          "domain",
          "status",
          "claimedAt"
        ],
        "properties": {
          "account": {
            "type": "string",
            "description": "The account holding it."
          },
          "claimedAt": {
            "type": "string",
            "format": "date-time",
            "description": "When it was claimed."
          },
          "domain": {
            "type": "string",
            "description": "The forward-order domain it reverses to, as recorded when the claim was made."
          },
          "namespace": {
            "type": "string",
            "description": "The namespace claimed."
          },
          "status": {
            "type": "string",
            "description": "`pending`, `verified` or `lapsed`."
          }
        }
      },
      "AdminPlanRecord": {
        "allOf": [
          {
            "$ref": "#/components/schemas/PlanRecord",
            "description": "Everything a customer sees."
          },
          {
            "type": "object",
            "required": [
              "updatedAt"
            ],
            "properties": {
              "stripePriceId": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "The provider's price object, where one is configured."
              },
              "updatedAt": {
                "type": "string",
                "format": "date-time",
                "description": "When the price last changed."
              }
            }
          }
        ],
        "description": "The same, plus what only staff should see."
      },
      "AgentProposal": {
        "type": "object",
        "description": "A proposal the agent produced (FR-830, ADR-0020).\n\nCarried as the authoring tier's own type. The agent does not get a parallel vocabulary:\nwhat it emits is a [`akp_project::ReviewDecision`], in the `Proposed` state, with a\nmachine author — and the server refuses anything else.",
        "required": [
          "fileSet",
          "decision"
        ],
        "properties": {
          "decision": {
            "type": "object",
            "description": "The decision itself.\n\nDescribed to `OpenAPI` as an opaque object rather than as a generated schema, for the\nsame reason [`crate::routes::ValidateRequest`] describes a package that way:\n`akp-project` carries no `utoipa` dependency, and a derive macro for an HTTP\nframework's documentation generator is precisely the transport concern ADR-0002 keeps\nout of the domain crates.",
            "additionalProperties": {},
            "propertyNames": {
              "type": "string"
            }
          },
          "fileSet": {
            "type": "string",
            "description": "Which file set it concerns."
          }
        }
      },
      "AiCapability": {
        "type": "object",
        "description": "What the agent tier offers this session (PRD-0009 §6.1).\n\nFour flags rather than an enum, and clippy is right to ask. They are genuinely independent:\na deployment can offer conversation without authoring, authoring without autonomy, and a\nvoice orthogonally to all three. Any enum over them would have more variants than fields and\nwould have to be widened by every future capability — which is how a wire type acquires a\ncombination nobody meant to allow.",
        "required": [
          "conversation",
          "authoringAssist",
          "autonomousAvailable"
        ],
        "properties": {
          "authoringAssist": {
            "type": "boolean",
            "description": "The authoring assistant is available (FR-820…FR-828)."
          },
          "autonomousAvailable": {
            "type": "boolean",
            "description": "Whether this account permits autonomous proposal application (FR-836).\n\nNote that this is *available*, not *on*: autonomy is a per-project flag in\n`akp-project`, and this only says the account has not forbidden it."
          },
          "budgetRemainingTokens": {
            "type": "integer",
            "format": "int64",
            "description": "Remaining budget for the current period, where the account has one (FR-849).\n\nReported so a client can show a user where they stand before a long operation, rather\nthan after it is refused mid-conversation.",
            "minimum": 0
          },
          "conversation": {
            "type": "boolean",
            "description": "The discovery conversation is available (FR-810)."
          },
          "voice": {
            "type": "boolean",
            "description": "Whether a hosted voice is configured for this deployment (ADR-0047).\n\nThe client reads this to decide whether to ask the server to speak or to use the\nbrowser's own synthesiser. False is a working state, not a degraded one — it is what\nevery deployment without a provider key gets, and invariant 13 requires it to keep\nworking."
          }
        }
      },
      "Alias": {
        "type": "object",
        "description": "An alias and what it points at.",
        "required": [
          "alias",
          "package",
          "description",
          "createdAt"
        ],
        "properties": {
          "alias": {
            "type": "string",
            "description": "The short name."
          },
          "createdAt": {
            "type": "string",
            "description": "When it was assigned, RFC 3339."
          },
          "description": {
            "type": "string",
            "description": "What it is for, in one line."
          },
          "package": {
            "type": "string",
            "description": "The package it names, as `namespace/name`."
          },
          "version": {
            "type": "string",
            "description": "What it currently resolves to, where anything does.\n\n`None` means nothing under it is visible to whoever asked — the package has no releases,\nor it has private ones and this caller is not its owner, or the target was a typo. The\nthree are one answer here for the same reason they are one answer in [`Aliases::resolve`].\n\nIt is on the listing because that is where a mistake gets noticed. Assigning an alias\ncannot check its target — `packages` has forced row-level security, so an unscoped read\ncannot see a private one and a check would refuse exactly the case staff most need — so\nthe feedback is \"here is what it resolves to\", which also catches the target that exists\nand has not been released yet."
          }
        }
      },
      "AliasInput": {
        "type": "object",
        "description": "What staff submit when assigning one.",
        "required": [
          "package"
        ],
        "properties": {
          "description": {
            "type": "string",
            "description": "What it is for. Optional."
          },
          "package": {
            "type": "string",
            "description": "The package to name, as `namespace/name`."
          }
        }
      },
      "ApiKey": {
        "type": "object",
        "description": "A key as anybody other than its creator ever sees it.",
        "required": [
          "id",
          "name",
          "prefix",
          "role",
          "createdAt"
        ],
        "properties": {
          "createdAt": {
            "type": "string",
            "description": "When it was issued."
          },
          "expiresAt": {
            "type": "string",
            "description": "When it stops working, where it does."
          },
          "id": {
            "type": "string",
            "description": "Stable identifier, for revocation."
          },
          "lastUsedAt": {
            "type": "string",
            "description": "When it was last presented, where it ever has been."
          },
          "name": {
            "type": "string",
            "description": "What the key is for, in the owner's words."
          },
          "prefix": {
            "type": "string",
            "description": "The first characters of the secret."
          },
          "revokedAt": {
            "type": "string",
            "description": "When it was revoked. A revoked key is listed, not hidden — \"when was it turned off?\" is\nasked during an incident."
          },
          "role": {
            "$ref": "#/components/schemas/Role",
            "description": "What this key may do."
          }
        }
      },
      "Appearance": {
        "type": "object",
        "description": "The house style: what a visitor gets before they choose anything.\n\nEvery field optional, and absent means \"no default — let the client decide\". That is a real\nstate and the one every deployment starts in, so it has to be expressible rather than papered\nover with a value copied from the front end.",
        "properties": {
          "font": {
            "type": "string"
          },
          "palette": {
            "type": "string"
          },
          "scale": {
            "type": "string"
          },
          "theme": {
            "type": "string",
            "description": "`light` or `dark`."
          }
        }
      },
      "AskRequest": {
        "type": "object",
        "description": "A question put to a deployment, and what it answered.",
        "required": [
          "question"
        ],
        "properties": {
          "history": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AskTurn"
            },
            "description": "What has already been said, oldest first.\n\nSent by the caller rather than held here, because a deployment is a service and not a\nsession: two people asking the same deployment things at once are two conversations, and\na thread stored against the deployment would braid them together. It also means the\nruntime keeps nothing about who asked what beyond the record FR-1079 requires.\n\nBounded on arrival — see [`HISTORY_LIMIT`]. A client that sent a thousand turns would\notherwise decide how much of the account's ceiling one question spends."
          },
          "question": {
            "type": "string",
            "description": "What was asked."
          }
        }
      },
      "AskResponse": {
        "type": "object",
        "description": "What a deployment answered, and what it answered from.",
        "required": [
          "passages",
          "unanswerable"
        ],
        "properties": {
          "cited": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "The knowledge objects the answer actually cited, in the order it used them.\n\nWhat it *used*, not what it was given: the runtime retrieves several and an answer may\nrest on one. FR-1079 records the first."
          },
          "passages": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Passage"
            },
            "description": "The passages the answer stands on, best first. Empty where the corpus has nothing."
          },
          "paused": {
            "type": [
              "string",
              "null"
            ],
            "description": "Set where this answer spent the period's ceiling and paused the deployment."
          },
          "reply": {
            "type": [
              "string",
              "null"
            ],
            "description": "The answer, written from the passages below (FR-1067).\n\nAbsent where no model is configured for this deployment. The passages are still returned\nand are still useful — a reader can see what matched — so the surface degrades to a\nsearch rather than to an error."
          },
          "unanswerable": {
            "type": "boolean",
            "description": "True where the deployment has nothing to say about this (FR-1068)."
          },
          "unavailable": {
            "type": "boolean",
            "description": "True where an answer was attempted and the model could not be reached.\n\nDistinct from `reply` being absent because none was configured. One is a fault somebody\ncan retry and report; the other is a deployment that will never write prose. Reporting\nthe first as the second is how a transient outage gets read as a missing feature."
          }
        }
      },
      "AskTurn": {
        "type": "object",
        "description": "One thing already said in this conversation.",
        "required": [
          "role",
          "content"
        ],
        "properties": {
          "content": {
            "type": "string",
            "description": "What was said."
          },
          "role": {
            "type": "string",
            "description": "`user` or `assistant`. Anything else is dropped rather than trusted."
          }
        }
      },
      "Assessment": {
        "type": "object",
        "description": "What the scorer concluded.",
        "required": [
          "score",
          "icp",
          "reasons"
        ],
        "properties": {
          "companyDomain": {
            "type": [
              "string",
              "null"
            ],
            "description": "The organisation's domain, where the address has a usable one."
          },
          "icp": {
            "type": "boolean",
            "description": "Whether this looks like somebody worth pursuing."
          },
          "reasons": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Which signals fired, in the order they were considered.\n\nKept because a score with no explanation is a number people either believe completely or\nignore completely, and both are wrong."
          },
          "score": {
            "type": "integer",
            "format": "int32",
            "description": "0 to 10, highest first."
          }
        }
      },
      "Asset": {
        "type": "object",
        "description": "A source asset (FR-004).\n\nOmitted relative to `domain/types.ts`, per rule 2 in the module docs: **`diagnostics`**.\nExtraction diagnostics are produced at ingestion (FR-013) and are authoring state held in\n`akp-project`. A published package does not carry them, and must validate, inspect and\ndiff with the project deleted (invariant 9). Serving an always-empty array would suggest\nthe registry had looked and found nothing, which is not what happened.",
        "required": [
          "id",
          "path",
          "mediaType",
          "storage",
          "integrity",
          "knowledgeObjectCount"
        ],
        "properties": {
          "byteLength": {
            "type": "integer",
            "format": "int64",
            "description": "Size in bytes. Absent where the package does not record one.",
            "minimum": 0
          },
          "digest": {
            "type": "string",
            "description": "Content hash. Absent for a referenced asset the publisher did not hash, which then\ncannot be checked for source drift (FR-074)."
          },
          "externalReference": {
            "type": "string",
            "description": "How to reach a referenced asset. Absent for an embedded one."
          },
          "id": {
            "type": "string",
            "description": "Identity within the package."
          },
          "integrity": {
            "$ref": "#/components/schemas/IntegrityState",
            "description": "Whether this asset's recorded digest was confirmed."
          },
          "knowledgeObjectCount": {
            "type": "integer",
            "format": "int32",
            "description": "Knowledge objects derived from this asset (the inverse provenance navigation).",
            "minimum": 0
          },
          "mediaType": {
            "type": "string",
            "description": "IANA media type of the source."
          },
          "path": {
            "type": "string",
            "description": "Where the bytes sit: the container entry for an embedded asset, the declared name for\na referenced one, which has no entry."
          },
          "storage": {
            "$ref": "#/components/schemas/AssetStorage",
            "description": "Embedded or referenced."
          }
        }
      },
      "AssetStorage": {
        "type": "string",
        "description": "Where an asset's bytes live (FR-070, FR-071).",
        "enum": [
          "embedded",
          "referenced"
        ]
      },
      "BillingAddress": {
        "type": "object",
        "description": "Where invoices go, and who they are addressed to.\n\nThe *account's*, not a person's. Three colleagues in one tenancy share one address, and putting\nit on `users` would give three copies that disagree the first time anybody moves office.",
        "properties": {
          "city": {
            "type": "string"
          },
          "country": {
            "type": "string",
            "description": "ISO 3166-1 alpha-2. The one standardised part, and the part tax turns on."
          },
          "email": {
            "type": "string",
            "description": "Where invoices go, where that is not the owner's own address."
          },
          "line1": {
            "type": "string"
          },
          "line2": {
            "type": "string"
          },
          "name": {
            "type": "string",
            "description": "Who to bill, where that is not the account's display name."
          },
          "postcode": {
            "type": "string"
          },
          "region": {
            "type": "string",
            "description": "County, state, province, prefecture. One slot on an envelope, whatever it is called."
          },
          "taxId": {
            "type": "string",
            "description": "VAT or equivalent. Free text: the formats differ by country and change."
          }
        }
      },
      "BillingCapability": {
        "type": "object",
        "description": "What the subscription surface offers this session.",
        "required": [
          "purchasable",
          "manageable"
        ],
        "properties": {
          "manageable": {
            "type": "boolean",
            "description": "Whether this session may change the subscription.\n\nOwner only. A member sees what the organisation is on and cannot cancel it."
          },
          "purchasable": {
            "type": "boolean",
            "description": "Whether a plan can actually be bought here.\n\nA present capability with `purchasable: false` is *not* a padlock: the surface exists —\nthis account has a subscription to read, a history to look at, and a plan it is on — and\nwhat is absent is the buy button. Telling the client that lets it render the page without\na control that would fail at the last step, which is different from rendering a disabled\ncontrol over a feature somebody could have."
          }
        }
      },
      "BillingEvent": {
        "type": "object",
        "description": "One line of the audit trail.",
        "required": [
          "kind",
          "createdAt"
        ],
        "properties": {
          "actor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Who, for a staff action. Absent for a provider event, which nobody here did."
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "description": "When."
          },
          "kind": {
            "type": "string",
            "description": "What happened."
          }
        }
      },
      "CallbackResponse": {
        "type": "object",
        "description": "A completed sign-in.",
        "required": [
          "token",
          "account",
          "accountCreated",
          "role"
        ],
        "properties": {
          "account": {
            "type": "string",
            "description": "The account this session acts for."
          },
          "accountCreated": {
            "type": "boolean",
            "description": "Whether this sign-in also created the account, i.e. whether it was a sign-up."
          },
          "role": {
            "$ref": "#/components/schemas/Role",
            "description": "What this person may do in that account. See [`PasswordSessionResponse::role`]."
          },
          "token": {
            "type": "string",
            "description": "The bearer token. **Shown once**; the database holds only a digest of it (FR-691)."
          }
        }
      },
      "CampaignCount": {
        "type": "object",
        "description": "One row of the campaign report.",
        "required": [
          "campaign",
          "kind",
          "total",
          "open"
        ],
        "properties": {
          "campaign": {
            "type": "string",
            "description": "The campaign token, or `(none)` for arrivals with no attribution."
          },
          "kind": {
            "type": "string",
            "description": "`signup` or `interest`."
          },
          "open": {
            "type": "integer",
            "format": "int64",
            "description": "How many are still `new` or `contacted`."
          },
          "total": {
            "type": "integer",
            "format": "int64",
            "description": "How many arrived."
          }
        }
      },
      "CancelRequest": {
        "type": "object",
        "description": "How to cancel.",
        "properties": {
          "atPeriodEnd": {
            "type": "boolean",
            "description": "Run to the end of the paid period rather than stopping now.\n\nDefaults to **true**, and the default is the whole ethic of the thing: they have paid for\nthe month, and ending it the moment somebody clicks is taking money for time not served.\nImmediate cancellation exists for staff issuing a refund alongside it."
          }
        }
      },
      "ChangeKind": {
        "type": "string",
        "description": "What happened to an item between two releases.",
        "enum": [
          "added",
          "changed",
          "removed"
        ]
      },
      "ChangeRequest": {
        "type": "object",
        "description": "An outstanding ask about a field somebody cannot edit.",
        "required": [
          "id",
          "field",
          "requested",
          "note",
          "status",
          "createdAt"
        ],
        "properties": {
          "createdAt": {
            "type": "string",
            "description": "When they asked, RFC 3339."
          },
          "field": {
            "$ref": "#/components/schemas/FixedField",
            "description": "Which field."
          },
          "id": {
            "type": "string",
            "description": "Its identifier."
          },
          "note": {
            "type": "string",
            "description": "Why, in their words. Empty where they gave no reason."
          },
          "requested": {
            "type": "string",
            "description": "What they would like it to say."
          },
          "status": {
            "$ref": "#/components/schemas/RequestStatus",
            "description": "Where it has got to."
          }
        }
      },
      "ChangeRequestInput": {
        "type": "object",
        "description": "What somebody is asking for.",
        "required": [
          "field",
          "requested"
        ],
        "properties": {
          "field": {
            "$ref": "#/components/schemas/FixedField",
            "description": "Which fixed field."
          },
          "note": {
            "type": "string",
            "description": "Why. Optional."
          },
          "requested": {
            "type": "string",
            "description": "What they would like it to be."
          }
        }
      },
      "CheckOutcome": {
        "type": "string",
        "description": "The outcome of a single conformance check.",
        "enum": [
          "pass",
          "advisory",
          "fail",
          "skipped"
        ]
      },
      "CheckoutRequest": {
        "type": "object",
        "description": "Which plan to buy.",
        "required": [
          "plan"
        ],
        "properties": {
          "plan": {
            "$ref": "#/components/schemas/Plan",
            "description": "The plan code."
          }
        }
      },
      "CheckoutStarted": {
        "type": "object",
        "description": "Where to send the browser.",
        "required": [
          "url"
        ],
        "properties": {
          "url": {
            "type": "string",
            "description": "The provider's hosted checkout."
          }
        }
      },
      "Citation": {
        "type": "object",
        "description": "Where a claim came from (FR-813).",
        "required": [
          "package",
          "knowledgeObject",
          "anchor"
        ],
        "properties": {
          "anchor": {
            "type": "string",
            "description": "The source anchor, rendered."
          },
          "knowledgeObject": {
            "type": "string",
            "description": "The knowledge object it came from."
          },
          "package": {
            "type": "string",
            "description": "The package the claim is about, as a coordinate label."
          }
        }
      },
      "ClaimRequest": {
        "type": "object",
        "description": "A namespace an account wishes to speak for.",
        "required": [
          "namespace"
        ],
        "properties": {
          "namespace": {
            "type": "string",
            "description": "The reverse-DNS namespace, e.g. `com.acme`."
          }
        }
      },
      "Cohort": {
        "type": "string",
        "description": "Which of the three groups an entry belongs to.\n\nDerived rather than stored, from `kind` and whether the account is paying. A stored cohort\nwould be a fourth thing to keep in step with the other three, and it would be wrong for the\nhour between somebody subscribing and anybody remembering to update it.\n\nThe three exist because the work is different for each. You do not send a \"would you like an\naccount?\" message to somebody who has one, and you do not chase a customer for a sale they\nhave already made.",
        "enum": [
          "interest",
          "registered",
          "customer"
        ]
      },
      "ComposedPackage": {
        "type": "object",
        "description": "One package that went into a deployment's corpus (FR-1057).\n\nThe evidence record, and the reason it is stored rather than recomputed: FR-083 asks what a\ndeployed system was authorised to use, and the honest answer names every package and digest\nat the moment it was seeded — not what the registry holds today.",
        "required": [
          "id",
          "version",
          "canonicalDigest",
          "root"
        ],
        "properties": {
          "canonicalDigest": {
            "type": "string",
            "description": "Its canonical digest, computed from that package."
          },
          "id": {
            "type": "string",
            "description": "The package identity."
          },
          "root": {
            "type": "boolean",
            "description": "Whether it is the bound release or one of its dependencies."
          },
          "version": {
            "type": "string",
            "description": "The exact version."
          }
        }
      },
      "CreateDeploymentRequest": {
        "type": "object",
        "description": "What an account asks for when hosting a bundle.",
        "required": [
          "name",
          "package",
          "version"
        ],
        "properties": {
          "name": {
            "type": "string",
            "description": "What to call it, in a list somebody will read."
          },
          "package": {
            "type": "string",
            "description": "The package identity to bind."
          },
          "tokenCeiling": {
            "type": [
              "integer",
              "null"
            ],
            "format": "int64",
            "description": "The period's token ceiling (ADR-0052 §2). Defaulted where it is not given."
          },
          "version": {
            "type": "string",
            "description": "The exact version. Never floating (FR-1055)."
          }
        }
      },
      "Delivery": {
        "type": "object",
        "description": "One recorded send, as the operator surface shows it.",
        "required": [
          "sentAt",
          "recipient",
          "kind",
          "sender",
          "subject",
          "outcome"
        ],
        "properties": {
          "detail": {
            "type": "string",
            "description": "The provider's own words, on failure."
          },
          "kind": {
            "type": "string",
            "description": "Which message: `welcome`, `reset`, `sales-acknowledgement` and so on."
          },
          "outcome": {
            "type": "string",
            "description": "`sent`, `failed`, or `logged` where this deployment has no provider configured."
          },
          "recipient": {
            "type": "string",
            "description": "Where it went."
          },
          "sender": {
            "type": "string",
            "description": "Which identity it went out as: `service`, `sales` or `support`."
          },
          "sentAt": {
            "type": "string",
            "description": "When it was attempted, RFC 3339."
          },
          "subject": {
            "type": "string",
            "description": "The subject line, as the recipient saw it."
          }
        }
      },
      "Deployment": {
        "type": "object",
        "description": "A deployment, as the account sees it.",
        "required": [
          "id",
          "name",
          "package",
          "version",
          "canonicalDigest",
          "adapter",
          "reachability",
          "status",
          "tokenCeiling",
          "tokensUsed",
          "runningSeconds",
          "itemCount",
          "createdAt"
        ],
        "properties": {
          "adapter": {
            "type": "string",
            "description": "Which adapter prepares and answers it."
          },
          "canonicalDigest": {
            "type": "string",
            "description": "The canonical digest it was bound to."
          },
          "composedFrom": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ComposedPackage"
            },
            "description": "What went into the corpus, where it has been prepared."
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "description": "When it was created."
          },
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "Stable identity."
          },
          "itemCount": {
            "type": "integer",
            "format": "int64",
            "description": "How many items the adapter indexed."
          },
          "name": {
            "type": "string",
            "description": "What a person called it."
          },
          "package": {
            "type": "string",
            "description": "The bound package."
          },
          "reachability": {
            "$ref": "#/components/schemas/Reachability",
            "description": "Who may reach it."
          },
          "runningSeconds": {
            "type": "integer",
            "format": "int64",
            "description": "Accrued running seconds, including the period in progress."
          },
          "status": {
            "$ref": "#/components/schemas/Status",
            "description": "Where it is in its life."
          },
          "statusReason": {
            "type": [
              "string",
              "null"
            ],
            "description": "Why, where the status alone does not say."
          },
          "tokenCeiling": {
            "type": "integer",
            "format": "int64",
            "description": "The period's token ceiling (ADR-0052 §2)."
          },
          "tokensUsed": {
            "type": "integer",
            "format": "int64",
            "description": "Tokens consumed against it this period, preparation included (FR-1085a)."
          },
          "version": {
            "type": "string",
            "description": "The bound release, exactly (FR-1055)."
          }
        }
      },
      "DerivedArtifact": {
        "type": "object",
        "description": "A derived artifact: a cache, never identity.\n\nDeleting every one of these leaves the canonical digest untouched (ADR-0003, invariant 2).\nThe UI states that explicitly because it is the single most misunderstood property of the\nformat, which is why [`DerivedArtifact::affects_canonical_identity`] is on the wire at all.\n\nOmitted relative to `domain/types.ts`, per rule 2 in the module docs: **`objectCount`**.\nCounting the vectors in an embedding profile means fetching the profile, and these routes\ndeliberately never do — a canonical-only read is a complete package (ADR-0008) and\ntransferring four gigabytes of vectors to render a count is precisely the cost FR-321\nexists to let a client decline.",
        "required": [
          "id",
          "kind",
          "profile",
          "model",
          "provider",
          "byteLength",
          "affectsCanonicalIdentity"
        ],
        "properties": {
          "affectsCanonicalIdentity": {
            "type": "boolean",
            "description": "Always `false`. Discarding this artifact leaves package identity unchanged."
          },
          "byteLength": {
            "type": "integer",
            "format": "int64",
            "description": "Transfer size, so a client can total the cost before starting it (FR-323).",
            "minimum": 0
          },
          "dimensions": {
            "type": "integer",
            "format": "int32",
            "description": "Vector dimensionality. Absent where the descriptor does not declare one.",
            "minimum": 0
          },
          "id": {
            "type": "string",
            "description": "Descriptor name within the release, e.g. `embedding/default`."
          },
          "kind": {
            "type": "string",
            "description": "What sort of derived artifact this is."
          },
          "model": {
            "type": "string",
            "description": "Generating model, so a consumer can tell whether its runtime can use these."
          },
          "profile": {
            "type": "string",
            "description": "Embedding profile identity, so several may coexist (FR-032)."
          },
          "provider": {
            "type": "string",
            "description": "Provider identifier."
          }
        }
      },
      "DerivedOfferView": {
        "type": "object",
        "description": "One derived artifact on offer, and what taking it would cost.",
        "required": [
          "name",
          "byteLength",
          "digest",
          "selected"
        ],
        "properties": {
          "byteLength": {
            "type": "integer",
            "format": "int64",
            "description": "Transfer size.",
            "minimum": 0
          },
          "digest": {
            "type": "string",
            "description": "Content address, so a client that already holds it can tell."
          },
          "name": {
            "type": "string",
            "description": "The descriptor name."
          },
          "profile": {
            "type": "string",
            "description": "Embedding profile identity, where it has one."
          },
          "selected": {
            "type": "boolean",
            "description": "Whether the requested selection includes it."
          }
        }
      },
      "DescriptorView": {
        "type": "object",
        "description": "One descriptor from a release manifest.\n\nNamed parts of the release, with their addresses and sizes and whether they are canonical.\nThis is what FR-320 means by retrieving the manifest before the content it describes: a\nclient can see the whole shape of a release, and decide, having fetched a few kilobytes.",
        "required": [
          "name",
          "mediaType",
          "digest",
          "byteLength",
          "role"
        ],
        "properties": {
          "byteLength": {
            "type": "integer",
            "format": "int64",
            "description": "Transfer size.",
            "minimum": 0
          },
          "digest": {
            "type": "string",
            "description": "Content address, under which it is stored and verified (FR-327)."
          },
          "dimensions": {
            "type": "integer",
            "format": "int32",
            "description": "Vector dimensionality, where declared.",
            "minimum": 0
          },
          "mediaType": {
            "type": "string",
            "description": "IANA media type of the blob."
          },
          "model": {
            "type": "string",
            "description": "Generating model, so a consumer can tell whether its runtime can use these (FR-323)."
          },
          "name": {
            "type": "string",
            "description": "Logical part name, stable across releases so two manifests can be compared."
          },
          "profile": {
            "type": "string",
            "description": "Embedding profile identity, for a derived descriptor that names one."
          },
          "role": {
            "type": "string",
            "description": "`canonical` or `derived` (ADR-0003)."
          }
        }
      },
      "DiffEntry": {
        "type": "object",
        "description": "One changed item between two releases.",
        "required": [
          "change",
          "id",
          "label"
        ],
        "properties": {
          "change": {
            "$ref": "#/components/schemas/ChangeKind",
            "description": "What happened to it."
          },
          "detail": {
            "type": "string",
            "description": "Why it is reported as changed, where the core says."
          },
          "fromDigest": {
            "type": "string",
            "description": "The item's digest in the earlier release."
          },
          "id": {
            "type": "string",
            "description": "The item's identity."
          },
          "label": {
            "type": "string",
            "description": "A short human label — an asset's name, or the opening of a knowledge object's text."
          },
          "toDigest": {
            "type": "string",
            "description": "The item's digest in the later release."
          }
        }
      },
      "DiffSummary": {
        "type": "object",
        "description": "Counts for one class of item.",
        "required": [
          "added",
          "changed",
          "removed",
          "unchanged"
        ],
        "properties": {
          "added": {
            "type": "integer",
            "format": "int32",
            "description": "Present in the later release only.",
            "minimum": 0
          },
          "changed": {
            "type": "integer",
            "format": "int32",
            "description": "Present in both, and different.",
            "minimum": 0
          },
          "removed": {
            "type": "integer",
            "format": "int32",
            "description": "Present in the earlier release only.",
            "minimum": 0
          },
          "unchanged": {
            "type": "integer",
            "format": "int32",
            "description": "Present in both and identical.",
            "minimum": 0
          }
        }
      },
      "DirectoryEntry": {
        "type": "object",
        "description": "One row of the cross-tenant list.",
        "required": [
          "account",
          "plan",
          "status",
          "provider",
          "cancelAtPeriodEnd",
          "monthlyAmountMinor",
          "currency"
        ],
        "properties": {
          "account": {
            "type": "string",
            "description": "The account."
          },
          "cancelAtPeriodEnd": {
            "type": "boolean",
            "description": "Cancelled, running to the end of the period."
          },
          "currency": {
            "type": "string",
            "description": "ISO 4217, lower case."
          },
          "currentPeriodEnd": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "End of the paid period."
          },
          "monthlyAmountMinor": {
            "type": "integer",
            "format": "int64",
            "description": "Monthly amount, from the plan this is on."
          },
          "plan": {
            "$ref": "#/components/schemas/Plan",
            "description": "What they are on."
          },
          "provider": {
            "type": "string",
            "description": "`stripe` or `manual`."
          },
          "status": {
            "$ref": "#/components/schemas/SubscriptionStatus",
            "description": "Where it stands."
          }
        }
      },
      "EmailRequest": {
        "type": "object",
        "description": "Just an address.",
        "required": [
          "email"
        ],
        "properties": {
          "email": {
            "type": "string",
            "description": "The address to act on."
          }
        }
      },
      "Entitlement": {
        "type": "object",
        "description": "An account's standing with respect to what it has paid for.",
        "required": [
          "plan",
          "privateRegistry"
        ],
        "properties": {
          "plan": {
            "$ref": "#/components/schemas/Plan",
            "description": "The plan on record."
          },
          "privateRegistry": {
            "type": "boolean",
            "description": "Whether the private registry is available right now.\n\nComputed rather than stored: a trial expires by the passage of time, and a stored boolean\nwould need a job to flip it — which means an account keeps its entitlement until that job\nruns, and loses it at a moment nobody can predict."
          },
          "trialEndsAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When the free month ends, where one is running."
          }
        }
      },
      "Entry": {
        "type": "object",
        "description": "One row of the funnel, as the admin surface sees it.",
        "required": [
          "id",
          "kind",
          "status",
          "note",
          "privateBundles",
          "publicBundles",
          "createdAt",
          "cohort",
          "scoreReasons"
        ],
        "properties": {
          "account": {
            "type": [
              "string",
              "null"
            ],
            "description": "The account slug, for a signup."
          },
          "campaign": {
            "type": [
              "string",
              "null"
            ],
            "description": "Where they came from."
          },
          "cohort": {
            "$ref": "#/components/schemas/Cohort",
            "description": "Which of the three groups this belongs to."
          },
          "company": {
            "type": [
              "string",
              "null"
            ],
            "description": "Their employer, where they gave one."
          },
          "companyDomain": {
            "type": [
              "string",
              "null"
            ],
            "description": "The organisation's domain, from the address."
          },
          "companyName": {
            "type": [
              "string",
              "null"
            ],
            "description": "The organisation, as determined rather than as typed into the form."
          },
          "companyNotes": {
            "type": [
              "string",
              "null"
            ],
            "description": "What is known about them, for whoever picks up the phone."
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "description": "When they arrived."
          },
          "email": {
            "type": [
              "string",
              "null"
            ],
            "description": "The address: given on the form for an interest, or the account's for a signup."
          },
          "icp": {
            "type": [
              "boolean",
              "null"
            ],
            "description": "Whether this looks like somebody worth pursuing."
          },
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "Stable identifier, for setting the status."
          },
          "kind": {
            "type": "string",
            "description": "`signup` or `interest`."
          },
          "leadScore": {
            "type": [
              "integer",
              "null"
            ],
            "format": "int32",
            "description": "0 to 10, highest first. Absent where nothing has scored it yet.\n\nAbsent is not zero. Zero is a judgement — we looked and there is nothing here — and absent\nmeans nobody has looked. The list sorts them differently for that reason."
          },
          "lostAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When it was lost."
          },
          "lostReason": {
            "type": [
              "string",
              "null"
            ],
            "description": "Why the deal was lost, where it was. Always present for a `lost` entry and never for\nanything else — the schema enforces the pair."
          },
          "name": {
            "type": [
              "string",
              "null"
            ],
            "description": "Their name, where they gave one."
          },
          "note": {
            "type": "string",
            "description": "What was said, by whoever last touched it."
          },
          "phone": {
            "type": [
              "string",
              "null"
            ],
            "description": "A number to call them back on, where they gave one. As typed."
          },
          "privateBundles": {
            "type": "integer",
            "format": "int32",
            "description": "How many bundles this account has in its private registry.\n\nZero for an interest registration, which has no account. The question it answers is whether\nsomebody is actually using the thing — a trial with forty private bundles is a different\nconversation from a trial with none."
          },
          "publicBundles": {
            "type": "integer",
            "format": "int32",
            "description": "How many it has published openly."
          },
          "scoreReasons": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Which signals produced the score."
          },
          "scoreSource": {
            "type": [
              "string",
              "null"
            ],
            "description": "`derived`, `assisted` or `manual` — see migration 0027."
          },
          "scoredAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When the score was arrived at."
          },
          "status": {
            "$ref": "#/components/schemas/Status",
            "description": "Where they are in the pipeline."
          }
        }
      },
      "EraseRequest": {
        "type": "object",
        "description": "Erase an account's personal data (ADR-0046).\n\nSoft: the account, its packages and their digests remain, so no coordinate anybody already\ndepends on stops resolving for a reason they could not have anticipated. What goes is the\npersonal data — and the status becomes `deleted`, which restricts as well.\n\n**Not reversible**, unlike a restriction. The UI asks for the slug to be typed for that\nreason; this refuses without it rather than trusting the client to have asked.",
        "required": [
          "confirm"
        ],
        "properties": {
          "confirm": {
            "type": "string",
            "description": "The account slug again, typed by the operator."
          }
        }
      },
      "ErrorBody": {
        "type": "object",
        "description": "The wire shape of an error.",
        "required": [
          "code",
          "message"
        ],
        "properties": {
          "code": {
            "type": "string",
            "description": "Stable machine-readable code."
          },
          "message": {
            "type": "string",
            "description": "Human-readable detail."
          }
        }
      },
      "EvaluationCase": {
        "type": "object",
        "description": "One evaluation case (FR-040, FR-041).",
        "required": [
          "id",
          "question",
          "expectedConcepts",
          "requiredEvidence"
        ],
        "properties": {
          "expectedConcepts": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Concepts an acceptable answer should contain."
          },
          "id": {
            "type": "string",
            "description": "Identity within the suite."
          },
          "question": {
            "type": "string",
            "description": "The question or task the knowledge should support."
          },
          "requiredEvidence": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Knowledge objects that must be cited as evidence. Validated to resolve (FR-052)."
          }
        }
      },
      "EvaluationCaseResult": {
        "type": "object",
        "description": "What happened to one case in one run.",
        "required": [
          "caseId",
          "outcome",
          "matchedConcepts",
          "missingConcepts",
          "citedEvidence",
          "missingEvidence",
          "latencyMs"
        ],
        "properties": {
          "caseId": {
            "type": "string",
            "description": "The case."
          },
          "citedEvidence": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Required evidence that was cited."
          },
          "latencyMs": {
            "type": "integer",
            "format": "int64",
            "description": "How long the case took.",
            "minimum": 0
          },
          "matchedConcepts": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Expected concepts the answer contained."
          },
          "missingConcepts": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Expected concepts it did not."
          },
          "missingEvidence": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Required evidence that was not."
          },
          "notes": {
            "type": "string",
            "description": "Anything the runner recorded."
          },
          "outcome": {
            "type": "string",
            "description": "`pass` or `fail`."
          }
        }
      },
      "EvaluationRun": {
        "type": "object",
        "description": "One execution of an evaluation suite against one target.\n\n**This type has no producer.** Nothing in this deployment executes an evaluation run:\nrunning a suite means standing up a target — an adapter, a model, a retrieval stack — and\nthat is PRD-0007's continuous evaluation service, which does not exist yet. The two routes\nthat return this shape are honest about it rather than absent: the list returns an empty\narray, because \"this release has no recorded runs\" is a true and useful answer, and\nfetching one by identity returns `404`, because no identity resolves.\n\nIt is defined here so the contract and the generated client are complete, and so that the\nservice which eventually produces runs has a shape to fill rather than one to negotiate.",
        "required": [
          "id",
          "packageId",
          "packageVersion",
          "suiteId",
          "target",
          "startedAt",
          "completedAt",
          "outcome",
          "passed",
          "failed",
          "total",
          "derivedArtifacts",
          "results"
        ],
        "properties": {
          "completedAt": {
            "type": "string",
            "description": "Completion time, RFC 3339."
          },
          "derivedArtifacts": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Which derived artifacts the run actually used (FR-083)."
          },
          "failed": {
            "type": "integer",
            "format": "int32",
            "description": "Cases failed.",
            "minimum": 0
          },
          "id": {
            "type": "string",
            "description": "Run identity."
          },
          "outcome": {
            "type": "string",
            "description": "`pass` or `fail`."
          },
          "packageId": {
            "type": "string",
            "description": "The package evaluated."
          },
          "packageVersion": {
            "type": "string",
            "description": "The version evaluated."
          },
          "passed": {
            "type": "integer",
            "format": "int32",
            "description": "Cases passed.",
            "minimum": 0
          },
          "results": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/EvaluationCaseResult"
            },
            "description": "Per-case results."
          },
          "startedAt": {
            "type": "string",
            "description": "Start time, RFC 3339."
          },
          "suiteId": {
            "type": "string",
            "description": "The suite executed."
          },
          "target": {
            "$ref": "#/components/schemas/EvaluationTarget",
            "description": "Where it ran."
          },
          "total": {
            "type": "integer",
            "format": "int32",
            "description": "Cases run.",
            "minimum": 0
          }
        }
      },
      "EvaluationTarget": {
        "type": "object",
        "description": "Where an evaluation run executed (FR-084).",
        "required": [
          "id",
          "name",
          "adapter",
          "deployment",
          "model",
          "retrieval"
        ],
        "properties": {
          "adapter": {
            "type": "string",
            "description": "Which adapter drove it."
          },
          "deployment": {
            "type": "string",
            "description": "`cloud` or `local`. Materially different targets are the portability proof (FR-084)."
          },
          "id": {
            "type": "string",
            "description": "Target identity."
          },
          "model": {
            "type": "string",
            "description": "Model identifier."
          },
          "name": {
            "type": "string",
            "description": "Human name."
          },
          "retrieval": {
            "type": "string",
            "description": "Retrieval configuration."
          }
        }
      },
      "FixedField": {
        "type": "string",
        "description": "Which fixed field somebody is asking to have changed.",
        "enum": [
          "email",
          "family-name"
        ]
      },
      "FunnelStatusChange": {
        "type": "object",
        "description": "A status change, with an optional note.",
        "required": [
          "status"
        ],
        "properties": {
          "note": {
            "type": [
              "string",
              "null"
            ],
            "description": "What was said. Replaces the previous note when given, and leaves it alone when absent."
          },
          "status": {
            "$ref": "#/components/schemas/Status",
            "description": "Where it moves to."
          }
        }
      },
      "GrantRequest": {
        "type": "object",
        "description": "What staff are granting, and until when.",
        "required": [
          "plan"
        ],
        "properties": {
          "plan": {
            "$ref": "#/components/schemas/Plan",
            "description": "The plan to put them on."
          },
          "until": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When it lapses. Absent means it does not, which is what an invoice-paying customer wants."
          }
        }
      },
      "IntegrityState": {
        "type": "string",
        "description": "Whether content was checked against its recorded digest, and what happened.",
        "enum": [
          "verified",
          "failed",
          "unchecked"
        ]
      },
      "IntegritySummary": {
        "type": "object",
        "description": "The result of verifying content against its recorded digests (FR-051).",
        "required": [
          "state",
          "algorithm",
          "verifiedAssets",
          "failedAssets",
          "checkedAt"
        ],
        "properties": {
          "algorithm": {
            "type": "string",
            "description": "Hash algorithm in use."
          },
          "checkedAt": {
            "type": "string",
            "description": "When the verification ran, RFC 3339."
          },
          "failedAssets": {
            "type": "integer",
            "format": "int32",
            "description": "Assets whose blobs did not.\n\nAlways `0` on a successful response, and that is a property of the read path rather\nthan optimism: reassembly verifies every blob as it reads it and fails the whole\nrequest on a mismatch (FR-327). A package that reaches this type is one where nothing\nfailed, because there is no third outcome where something almost right is served.",
            "minimum": 0
          },
          "state": {
            "$ref": "#/components/schemas/IntegrityState",
            "description": "Overall outcome."
          },
          "verifiedAssets": {
            "type": "integer",
            "format": "int32",
            "description": "Assets whose blobs matched their descriptors.",
            "minimum": 0
          }
        }
      },
      "InterestRequest": {
        "type": "object",
        "description": "Somebody asking to be contacted about a private registry.",
        "required": [
          "email"
        ],
        "properties": {
          "campaign": {
            "type": [
              "string",
              "null"
            ],
            "description": "Where the form was reached from."
          },
          "company": {
            "type": [
              "string",
              "null"
            ],
            "description": "Their employer."
          },
          "email": {
            "type": "string",
            "description": "Where to reply. The only field that is required."
          },
          "name": {
            "type": [
              "string",
              "null"
            ],
            "description": "Their name."
          },
          "phone": {
            "type": [
              "string",
              "null"
            ],
            "description": "A number to call them back on. Stored as typed and never parsed (migration 0031)."
          }
        }
      },
      "Invitation": {
        "type": "object",
        "description": "Somebody invited who has not registered yet.",
        "required": [
          "email",
          "role",
          "expiresAt"
        ],
        "properties": {
          "email": {
            "type": "string",
            "description": "The address invited."
          },
          "expiresAt": {
            "type": "string",
            "format": "date-time",
            "description": "When the invitation stops being redeemable."
          },
          "role": {
            "$ref": "#/components/schemas/Role",
            "description": "What they will be able to do when they arrive."
          }
        }
      },
      "IssuedKey": {
        "type": "object",
        "description": "A newly issued key, and the one moment its secret exists outside the caller's memory.",
        "required": [
          "key",
          "secret"
        ],
        "properties": {
          "key": {
            "$ref": "#/components/schemas/ApiKey",
            "description": "The record."
          },
          "secret": {
            "type": "string",
            "description": "The secret, in full. Never retrievable again."
          }
        }
      },
      "KnowledgeObject": {
        "type": "object",
        "description": "A normalised unit of knowledge (FR-012, FR-020).\n\nOmitted relative to `domain/types.ts`, per rule 2 in the module docs: **`tokenEstimate`**.\n`akp-core` has no token estimator, and a `characters / 4` guess made here would be a\ndifferent number from whatever the runtime actually tokenising this reports — a fabricated\nfigure that a context-window decision would then be made against.\n\n`metadata` is a deliberate difference too, and this one is a correction rather than a gap.\nThe front-end draft declared three fixed arrays — `equipment`, `concepts`,\n`classification`. The format defines no such fields: FR-022 makes knowledge-object\nmetadata arbitrary and namespaced, so flattening it into three keys would have invented a\nschema and silently dropped every key that did not fit one of them.",
        "required": [
          "id",
          "content",
          "metadata",
          "digest"
        ],
        "properties": {
          "anchorLabel": {
            "type": "string",
            "description": "Short human label for the anchor, e.g. `page 147`, for list rendering."
          },
          "assetId": {
            "type": "string",
            "description": "The asset this came from. Absent only for an object with no provenance, which is\nnon-conformant (FR-011) and still has to be displayable so somebody can fix it."
          },
          "assetPath": {
            "type": "string",
            "description": "That asset's path."
          },
          "content": {
            "type": "string",
            "description": "The normalised text."
          },
          "digest": {
            "type": "string",
            "description": "Digest of the normalised text.\n\nA content hash over `content` alone, for display and comparison. **Not** package\nidentity and not the object's identity — identity is the content-derived `id`, and\nthe canonical digest is a Merkle root over the whole package (ADR-0003)."
          },
          "id": {
            "type": "string",
            "description": "Content-derived stable identity, so reprocessing the same source yields the same id."
          },
          "metadata": {
            "type": "object",
            "description": "Namespaced metadata, verbatim (FR-022).",
            "additionalProperties": {},
            "propertyNames": {
              "type": "string"
            }
          }
        }
      },
      "Manifest": {
        "type": "object",
        "description": "The manifest as the core reports it.\n\n`extensions` carries namespaced keys this build does not understand. They are returned\nverbatim and never rewritten: a consumer that drops an extension it does not understand\nbreaks round-tripping (FR-014, invariant 8).",
        "required": [
          "akpVersion",
          "package",
          "publisher",
          "description",
          "languages",
          "capabilities",
          "content",
          "evaluation",
          "integrity",
          "extensions"
        ],
        "properties": {
          "akpVersion": {
            "type": "string",
            "description": "Specification version the package declares."
          },
          "capabilities": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Capability claims."
          },
          "content": {
            "$ref": "#/components/schemas/ManifestContent",
            "description": "What kind of content is carried."
          },
          "description": {
            "type": "string",
            "description": "Free-text description. Empty where the publisher wrote none."
          },
          "evaluation": {
            "$ref": "#/components/schemas/ManifestEvaluation",
            "description": "Evaluation suite presence."
          },
          "extensions": {
            "type": "object",
            "description": "Namespaced extensions, preserved byte-for-byte.",
            "additionalProperties": {},
            "propertyNames": {
              "type": "string"
            }
          },
          "integrity": {
            "$ref": "#/components/schemas/ManifestIntegrity",
            "description": "Integrity claims."
          },
          "languages": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "BCP-47 language tags."
          },
          "licence": {
            "type": "string",
            "description": "Declared licence. Absent means the publisher declared none."
          },
          "package": {
            "$ref": "#/components/schemas/ManifestPackage",
            "description": "Identity block."
          },
          "publisher": {
            "$ref": "#/components/schemas/Publisher",
            "description": "Publisher."
          }
        }
      },
      "ManifestContent": {
        "type": "object",
        "description": "What kind of content the package carries.",
        "required": [
          "embedded",
          "externalReferences"
        ],
        "properties": {
          "embedded": {
            "type": "boolean",
            "description": "At least one asset's bytes travel inside the artifact."
          },
          "externalReferences": {
            "type": "boolean",
            "description": "At least one asset must be resolved from elsewhere, so the artifact alone is not\nsufficient (FR-073)."
          }
        }
      },
      "ManifestEvaluation": {
        "type": "object",
        "description": "Whether the package brings an evaluation suite (FR-042).",
        "required": [
          "present",
          "caseCount"
        ],
        "properties": {
          "caseCount": {
            "type": "integer",
            "format": "int32",
            "description": "How many cases it carries.",
            "minimum": 0
          },
          "present": {
            "type": "boolean",
            "description": "Whether a suite is present at all."
          }
        }
      },
      "ManifestIntegrity": {
        "type": "object",
        "description": "The package's integrity claims.",
        "required": [
          "algorithm",
          "canonicalDigest",
          "releaseDigest",
          "signed"
        ],
        "properties": {
          "algorithm": {
            "type": "string",
            "description": "Hash algorithm. Only `sha256` is defined at specification version 0.1."
          },
          "canonicalDigest": {
            "type": "string",
            "description": "Merkle root over canonical entries — this is package identity (ADR-0003)."
          },
          "releaseDigest": {
            "type": "string",
            "description": "Digest of the release manifest that named the blobs this was rebuilt from."
          },
          "signature": {
            "$ref": "#/components/schemas/SignatureDetail",
            "description": "The signature itself, as the publisher made it. Absent means unsigned.\n\nReported in full rather than summarised, because a summary is not checkable. Everything\nneeded to verify offline is here — the algorithm, the key, the digest that was covered\nand the signature bytes — so a reader who does not trust this registry's word can take\nthe artifact and the key and settle it themselves."
          },
          "signed": {
            "type": "boolean",
            "description": "Whether a publisher signature was found.\n\nRead from what the registry recorded at publish, not from the reassembled bytes: the\nrelease manifest carries no signature block, so a package rebuilt from blobs has none\nto find. The archive endpoint writes this same recorded signature back into the\nartifact it hands out, which is what keeps this field and `akp verify` agreeing."
          },
          "signer": {
            "type": "string",
            "description": "The signing key, where there is a signature. Absent means unsigned."
          }
        }
      },
      "ManifestPackage": {
        "type": "object",
        "description": "The manifest's identity block.",
        "required": [
          "id",
          "name",
          "version"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "Package identity."
          },
          "name": {
            "type": "string",
            "description": "Human-readable name."
          },
          "version": {
            "type": "string",
            "description": "Declared version."
          }
        }
      },
      "ManualAssessment": {
        "type": "object",
        "description": "A judgement a person is recording.\n\nEvery field optional: somebody correcting a company name should not have to restate a score,\nand a form that sent every field back would overwrite a colleague's edit with whatever it\nhappened to have loaded.",
        "properties": {
          "companyName": {
            "type": [
              "string",
              "null"
            ],
            "description": "The organisation."
          },
          "companyNotes": {
            "type": [
              "string",
              "null"
            ],
            "description": "What is known about them."
          },
          "icp": {
            "type": [
              "boolean",
              "null"
            ],
            "description": "Whether they are worth pursuing."
          },
          "score": {
            "type": [
              "integer",
              "null"
            ],
            "format": "int32",
            "description": "0 to 10."
          }
        }
      },
      "Member": {
        "type": "object",
        "description": "A member of an account.",
        "required": [
          "userId",
          "email",
          "name",
          "role",
          "joinedAt"
        ],
        "properties": {
          "email": {
            "type": "string",
            "description": "Their email address."
          },
          "joinedAt": {
            "type": "string",
            "description": "When they joined."
          },
          "name": {
            "type": "string",
            "description": "Their display name."
          },
          "role": {
            "$ref": "#/components/schemas/Role",
            "description": "What they may do here."
          },
          "userId": {
            "type": "string",
            "description": "The platform's stable identifier for the person."
          }
        }
      },
      "Mode": {
        "type": "string",
        "description": "How a recipient is meant to get the bundle.",
        "enum": [
          "direct",
          "portal"
        ]
      },
      "NamespaceClaimView": {
        "type": "object",
        "description": "A claim, and what to do about it.",
        "required": [
          "namespace",
          "domain",
          "recordName",
          "recordValue",
          "status",
          "method"
        ],
        "properties": {
          "domain": {
            "type": "string",
            "description": "The domain whose control proves it."
          },
          "method": {
            "type": "string",
            "description": "`dns-txt`, or `manual` where an operator recorded it out of band."
          },
          "namespace": {
            "type": "string",
            "description": "The claimed namespace."
          },
          "recordName": {
            "type": "string",
            "description": "The DNS name the TXT record goes at."
          },
          "recordValue": {
            "type": "string",
            "description": "The exact value that record must carry.\n\nWhole, not a token to assemble. Assembling it is where a claim goes wrong, and the\nperson doing it is pasting into a zone file rather than writing code."
          },
          "status": {
            "type": "string",
            "description": "`pending`, `verified` or `lapsed`."
          },
          "tokenExpiresAt": {
            "type": "string",
            "description": "When an unused token stops being good, RFC 3339 (FR-1037)."
          },
          "verifiedAt": {
            "type": "string",
            "description": "When the proof was accepted, RFC 3339. Absent while pending."
          }
        }
      },
      "NewApiKey": {
        "type": "object",
        "description": "What a key should be (FR-1040).",
        "required": [
          "name"
        ],
        "properties": {
          "expiresInDays": {
            "type": "integer",
            "format": "int64",
            "description": "Days until it stops working. Omitted means it does not expire."
          },
          "name": {
            "type": "string",
            "description": "What it is for, in the owner's words."
          },
          "role": {
            "$ref": "#/components/schemas/Role",
            "description": "How far it reaches. Never further than the caller's own role."
          }
        }
      },
      "NewMember": {
        "type": "object",
        "description": "Who to add, and as what.",
        "required": [
          "email"
        ],
        "properties": {
          "email": {
            "type": "string",
            "description": "Their email address. They must already have signed in once."
          },
          "role": {
            "$ref": "#/components/schemas/Role",
            "description": "What they may do."
          }
        }
      },
      "PackageDetail": {
        "type": "object",
        "description": "Everything a package detail view renders.",
        "required": [
          "summary",
          "manifest",
          "assets",
          "derivedArtifacts",
          "integrity",
          "lineage"
        ],
        "properties": {
          "assets": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Asset"
            },
            "description": "Source assets."
          },
          "derivedArtifacts": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/DerivedArtifact"
            },
            "description": "Derived artifacts the release carries, named but not fetched."
          },
          "integrity": {
            "$ref": "#/components/schemas/IntegritySummary",
            "description": "What verification found."
          },
          "lineage": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PackageVersionRef"
            },
            "description": "Every version of this package, newest first (FR-063)."
          },
          "manifest": {
            "$ref": "#/components/schemas/Manifest",
            "description": "The manifest."
          },
          "summary": {
            "$ref": "#/components/schemas/PackageSummary",
            "description": "The listing summary, with a real conformance verdict — this route reassembled the\npackage, so it can answer."
          }
        }
      },
      "PackageDiff": {
        "type": "object",
        "description": "The difference between two releases of one package (FR-062).\n\nOmitted relative to `domain/types.ts`, per rule 2 in the module docs: **`manifestChanges`**.\n`akp_core::diff` compares assets, knowledge objects and chunking configuration; FR-062\ndoes not extend to manifest field changes and the core computes none. Comparing manifest\nfields *here* would be a diff rule implemented in a handler, which FR-665 forbids and\nwhich would then disagree with the CLI. It belongs in `akp-core` as an extension of\n[`akp_core::diff::Diff`], and until it is there this field has no honest producer.",
        "required": [
          "packageId",
          "from",
          "to",
          "assets",
          "knowledgeObjects",
          "assetSummary",
          "knowledgeObjectSummary",
          "chunkingChanged",
          "canonicalChanged"
        ],
        "properties": {
          "assetSummary": {
            "$ref": "#/components/schemas/DiffSummary",
            "description": "Asset counts."
          },
          "assets": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/DiffEntry"
            },
            "description": "Asset changes."
          },
          "canonicalChanged": {
            "type": "boolean",
            "description": "Whether the canonical digest differs at all."
          },
          "chunkingChanged": {
            "type": "boolean",
            "description": "Whether the chunking configuration changed, which explains wholesale churn without\nimplying the source material moved."
          },
          "from": {
            "$ref": "#/components/schemas/PackageVersionRef",
            "description": "The earlier release."
          },
          "knowledgeObjectSummary": {
            "$ref": "#/components/schemas/DiffSummary",
            "description": "Knowledge object counts."
          },
          "knowledgeObjects": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/DiffEntry"
            },
            "description": "Knowledge object changes.\n\nBecause knowledge-object identity is content-derived, an edited chunk appears as a\nremoval plus an addition rather than a modification. That is the honest\nrepresentation: the old text and the new text are different knowledge, and a review\napproving the former does not carry over."
          },
          "packageId": {
            "type": "string",
            "description": "Package identity."
          },
          "to": {
            "$ref": "#/components/schemas/PackageVersionRef",
            "description": "The later release."
          }
        }
      },
      "PackageSummary": {
        "type": "object",
        "description": "A package version as a listing row renders it.\n\nEvery field here comes from one database row and no blob (see migration 0006). That is\nwhat makes a front page of several hundred rows one query rather than several hundred\nreassemblies.\n\nOmitted relative to `domain/types.ts`, per rule 2 in the module docs: nothing.",
        "required": [
          "id",
          "name",
          "version",
          "publisher",
          "visibility",
          "summary",
          "languages",
          "capabilities",
          "validationStatus",
          "canonicalDigest",
          "assetCount",
          "knowledgeObjectCount",
          "evaluationCaseCount",
          "addedAt",
          "facets",
          "conceptCount",
          "requires",
          "bundleCount",
          "bundleIds"
        ],
        "properties": {
          "addedAt": {
            "type": "string",
            "description": "When this release reached this registry, RFC 3339.\n\nDistinct from [`PackageSummary::released_at`], which is the *publisher's* date and\nbelongs to the thing described — OASIS AMQP says 2012 because that is when OASIS\npublished it. This is a registry fact, always present, and it is what \"recently added\"\ncan honestly be ordered by: thirty of the seed bundles describe nothing external and\nhave no publisher date at all."
          },
          "assetCount": {
            "type": "integer",
            "format": "int32",
            "description": "Assets in the package.",
            "minimum": 0
          },
          "bundleCount": {
            "type": "integer",
            "format": "int32",
            "description": "Bundles carried (ADR-0022). One is the ordinary case.",
            "minimum": 0
          },
          "bundleIds": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Bundle identities inside, so search can match on them."
          },
          "canonicalDigest": {
            "type": "string",
            "description": "Identity over canonical content (ADR-0003)."
          },
          "capabilities": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Capability claims. Claims by the publisher, not guarantees — what evaluation suites\nexist to test (FR-044)."
          },
          "conceptCount": {
            "type": "integer",
            "format": "int32",
            "description": "Vocabulary concepts the package defines (ADR-0025).",
            "minimum": 0
          },
          "evaluationCaseCount": {
            "type": "integer",
            "format": "int32",
            "description": "Evaluation cases in the package.",
            "minimum": 0
          },
          "facets": {
            "type": "object",
            "description": "The publisher's own classification, verbatim from the manifest (ADR-0028).\n\nFree-form on the wire because it is a *namespaced extension*: the publisher owns the\nshape, and invariant 8 says a consumer that does not understand one preserves it rather\nthan normalising it away. Typing it here would quietly drop any key the seed corpus's\nvocabulary has not thought of yet, which is the opposite of what an extension point is\nfor.\n\nEmpty for a package that declares no classification, which is most of them.",
            "additionalProperties": {},
            "propertyNames": {
              "type": "string"
            }
          },
          "id": {
            "type": "string",
            "description": "Reverse-DNS package identity (FR-001). A self-describing label, never a pointer."
          },
          "knowledgeObjectCount": {
            "type": "integer",
            "format": "int32",
            "description": "Knowledge objects in the package.",
            "minimum": 0
          },
          "languages": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "BCP-47 language tags present in the knowledge."
          },
          "name": {
            "type": "string",
            "description": "Human-readable name from the manifest."
          },
          "provisionCount": {
            "type": "integer",
            "format": "int64",
            "description": "How many times this has been pulled from the registry (FR-321).\n\nReal, and counted per release by the archive route. Absent only where the listing did\nnot carry one — never `0` standing in for \"we do not know\", because \"never\nprovisioned\" and \"provisioned zero times since we started counting\" are different\nclaims and a reader cannot tell them apart from a number.\n\nA provision is one *pull*, not one file and not a byte total: a canonical-only pull\nthat declines four gigabytes of embeddings is still one provision, and counting bytes\nwould make this a measure of embedding size rather than of use.",
            "minimum": 0
          },
          "publisher": {
            "$ref": "#/components/schemas/Publisher",
            "description": "Publisher and namespace."
          },
          "releasedAt": {
            "type": "string",
            "description": "When this version was published to a registry, RFC 3339.\n\nAbsent means never published. Always present on these routes, because a release is\nthe only thing they serve; kept optional because the same type describes a package\ncompiled locally, which has no publication date and must not be given a fabricated\none."
          },
          "requires": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Bundle coordinates this package declares a dependency on (ADR-0024)."
          },
          "shared": {
            "type": "boolean",
            "description": "Whether this reader reaches it through a share rather than owning it (PRD-0011).\n\nOn the wire for the same reason `visibility` is: a private bundle on somebody's shelf is\nnormally theirs, one that arrived by a share is not, and a list that presents the two\nidentically tells a reader they published something they did not.\n\nSkipped when false, which is the ordinary case and keeps it out of every public listing."
          },
          "summary": {
            "type": "string",
            "description": "The manifest's free-text description. Empty where the publisher wrote none."
          },
          "validationStatus": {
            "$ref": "#/components/schemas/ValidationStatus",
            "description": "Conformance, where anything has established it.\n\n[`ValidationStatus::Unvalidated`] on any route that did not reassemble the package."
          },
          "version": {
            "type": "string",
            "description": "The released version (FR-061)."
          },
          "visibility": {
            "type": "string",
            "description": "Which registry this belongs to: `public` or `private` (FR-1015).\n\nOn the wire because \"the registry a package belongs to is visible at a glance wherever\npackages are shown\", and because a listing that mixes the two without saying which is\nhow somebody shares a coordinate their colleagues cannot resolve."
          }
        }
      },
      "PackageVersionRef": {
        "type": "object",
        "description": "A reference to one version of a package.",
        "required": [
          "id",
          "version",
          "canonicalDigest",
          "validationStatus"
        ],
        "properties": {
          "canonicalDigest": {
            "type": "string",
            "description": "Identity over canonical content."
          },
          "id": {
            "type": "string",
            "description": "Package identity."
          },
          "releasedAt": {
            "type": "string",
            "description": "Publication time, RFC 3339. Absent for anything never published."
          },
          "validationStatus": {
            "$ref": "#/components/schemas/ValidationStatus",
            "description": "Conformance, where anything has established it. See [`ValidationStatus::Unvalidated`]."
          },
          "version": {
            "type": "string",
            "description": "The version."
          }
        }
      },
      "PageContext": {
        "type": "object",
        "description": "The page the question was asked from.\n\nOrientation, not authority. \"It\" and \"this bundle\" are the most common words in a question\nasked from a package page, and without this the assistant has to ask which one — or guess.\n\nIt widens nothing. The coordinate here is a label the client supplies, and the tools still\nact with the caller's own credential (ADR-0036): naming a package you cannot read gets the\nsame refusal as asking for it by name.",
        "required": [
          "path"
        ],
        "properties": {
          "capabilities": {
            "type": "string",
            "description": "What somebody can do on it, one per line."
          },
          "description": {
            "type": "string",
            "description": "What the page is for, in one sentence.\n\nSupplied by the client rather than known here, and that is the right way round: the\npages belong to the client, and a server-side copy of what each one does would be a\nsecond description to keep in step with the first."
          },
          "package": {
            "type": "string",
            "description": "The package coordinate in view, where the page is about one."
          },
          "path": {
            "type": "string",
            "description": "The route, as the client knows it — `/packages/com.acme.procedures/3.2.1`."
          },
          "title": {
            "type": "string",
            "description": "What the page calls itself, where it has a title."
          },
          "version": {
            "type": "string",
            "description": "The version in view, alongside `package`."
          }
        }
      },
      "Passage": {
        "type": "object",
        "description": "One passage the runtime can answer from.",
        "required": [
          "id",
          "content",
          "anchor",
          "package"
        ],
        "properties": {
          "anchor": {
            "type": "string",
            "description": "Where it came from: the asset and the locator within it (FR-011)."
          },
          "content": {
            "type": "string",
            "description": "The text itself."
          },
          "id": {
            "type": "string",
            "description": "The knowledge object's identity, which is what a citation names (FR-1067)."
          },
          "package": {
            "type": "string",
            "description": "Which package in the composed corpus it came from."
          }
        }
      },
      "PasswordRequest": {
        "type": "object",
        "description": "An address and a password.",
        "required": [
          "email",
          "password"
        ],
        "properties": {
          "email": {
            "type": "string",
            "description": "The address on record."
          },
          "password": {
            "type": "string",
            "description": "The password."
          }
        }
      },
      "PasswordSessionResponse": {
        "type": "object",
        "description": "What either password door hands back.",
        "required": [
          "token",
          "account",
          "accountCreated",
          "emailVerified",
          "role"
        ],
        "properties": {
          "account": {
            "type": "string",
            "description": "The account this session acts for."
          },
          "accountCreated": {
            "type": "boolean",
            "description": "Whether this also created the account."
          },
          "emailVerified": {
            "type": "boolean",
            "description": "Whether the address has been proved (FR-688). `false` means the nag, not a lock."
          },
          "role": {
            "$ref": "#/components/schemas/Role",
            "description": "What this person may do in that account.\n\nSent so the client does not have to guess. It did, and it guessed wrong for everybody: with\nno role on this response the stored session had none, the client fell back to `member`, and\nthe person who had just created the account — its owner in `account_members` — got a client\nwith no Members entry, no API keys, no namespaces and an upload page refusing them."
          },
          "token": {
            "type": "string",
            "description": "The bearer token. **Shown once**; the database holds only a digest (FR-691)."
          }
        }
      },
      "PaymentRecord": {
        "type": "object",
        "description": "One payment.",
        "required": [
          "id",
          "amountMinor",
          "refundedMinor",
          "currency",
          "status",
          "description",
          "createdAt",
          "refundableMinor"
        ],
        "properties": {
          "amountMinor": {
            "type": "integer",
            "format": "int64",
            "description": "Minor units taken."
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "description": "When it was taken."
          },
          "currency": {
            "type": "string",
            "description": "ISO 4217, lower case."
          },
          "description": {
            "type": "string",
            "description": "What it was for."
          },
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "Our identifier, which is what a refund is requested against."
          },
          "invoiceId": {
            "type": [
              "string",
              "null"
            ],
            "description": "The provider's invoice, for reconciliation."
          },
          "refundableMinor": {
            "type": "integer",
            "format": "int64",
            "description": "How much of it could still be given back."
          },
          "refundedMinor": {
            "type": "integer",
            "format": "int64",
            "description": "Minor units given back."
          },
          "status": {
            "type": "string",
            "description": "`succeeded`, `refunded`, `partially-refunded` or `failed`."
          }
        }
      },
      "PendingChange": {
        "allOf": [
          {
            "$ref": "#/components/schemas/ChangeRequest",
            "description": "What was asked."
          },
          {
            "type": "object",
            "required": [
              "userId",
              "email",
              "name"
            ],
            "properties": {
              "email": {
                "type": "string",
                "description": "Their address as it stands."
              },
              "name": {
                "type": "string",
                "description": "Their name."
              },
              "userId": {
                "type": "string",
                "description": "Who asked."
              }
            }
          }
        ],
        "description": "An outstanding request, with enough about the person to action it."
      },
      "Plan": {
        "type": "string",
        "description": "What an account is on.",
        "enum": [
          "trial",
          "free",
          "team"
        ]
      },
      "PlanRecord": {
        "type": "object",
        "description": "A plan, as the catalogue holds it.",
        "required": [
          "code",
          "name",
          "description",
          "monthlyAmountMinor",
          "currency",
          "purchasable",
          "active"
        ],
        "properties": {
          "active": {
            "type": "boolean",
            "description": "Whether it is offered at all."
          },
          "code": {
            "$ref": "#/components/schemas/Plan",
            "description": "`trial`, `free` or `team`."
          },
          "currency": {
            "type": "string",
            "description": "ISO 4217, lower case."
          },
          "description": {
            "type": "string",
            "description": "A sentence about what it includes."
          },
          "monthlyAmountMinor": {
            "type": "integer",
            "format": "int64",
            "description": "Minor units per month. Pence, not pounds."
          },
          "name": {
            "type": "string",
            "description": "What a customer sees."
          },
          "purchasable": {
            "type": "boolean",
            "description": "Whether this plan can be bought right now.\n\nComputed rather than stored: a plan is purchasable when it is active, costs something, and\nhas a provider price to charge against. A client that had to work that out from three\nfields would work it out differently from the server on the first edge case."
          }
        }
      },
      "PlanUpdate": {
        "type": "object",
        "description": "What an administrator may change about a plan.\n\nEvery field optional: an edit that only changes the price should not have to restate the\ndescription, and a form that sent every field back would overwrite a concurrent edit with\nwhatever it happened to have loaded.",
        "properties": {
          "active": {
            "type": [
              "boolean",
              "null"
            ],
            "description": "Whether it is offered."
          },
          "currency": {
            "type": [
              "string",
              "null"
            ],
            "description": "ISO 4217, lower case."
          },
          "description": {
            "type": [
              "string",
              "null"
            ],
            "description": "A sentence about what it includes."
          },
          "monthlyAmountMinor": {
            "type": [
              "integer",
              "null"
            ],
            "format": "int64",
            "description": "Minor units per month."
          },
          "name": {
            "type": [
              "string",
              "null"
            ],
            "description": "What a customer sees."
          },
          "stripePriceId": {
            "type": [
              "string",
              "null"
            ],
            "description": "The provider's price object. An explicit empty string clears it."
          }
        }
      },
      "Profile": {
        "type": "object",
        "description": "Somebody's own details, as the settings screen shows them.",
        "required": [
          "email",
          "emailVerified",
          "displayName",
          "marketingOptIn",
          "pending"
        ],
        "properties": {
          "companyName": {
            "type": [
              "string",
              "null"
            ],
            "description": "Where they work, as they describe it."
          },
          "displayName": {
            "type": "string",
            "description": "What they are shown as next to their name in a member list."
          },
          "email": {
            "type": "string",
            "description": "The address the account is identified by. Read-only."
          },
          "emailVerified": {
            "type": "boolean",
            "description": "Whether they have proved it."
          },
          "familyName": {
            "type": [
              "string",
              "null"
            ],
            "description": "Their surname."
          },
          "givenName": {
            "type": [
              "string",
              "null"
            ],
            "description": "Their first name."
          },
          "jobTitle": {
            "type": [
              "string",
              "null"
            ],
            "description": "What they do there."
          },
          "marketingDecidedAt": {
            "type": [
              "string",
              "null"
            ],
            "description": "When they last decided either way, RFC 3339. `None` where they never have — which is a\ndifferent state from \"said no\", and worth being able to tell apart."
          },
          "marketingOptIn": {
            "type": "boolean",
            "description": "Whether they have said yes to product mail. Starts false: consent is opt-in."
          },
          "pending": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ChangeRequest"
            },
            "description": "Anything they have asked to have changed and nobody has settled yet."
          },
          "phone": {
            "type": [
              "string",
              "null"
            ],
            "description": "A number to reach them on. As typed."
          }
        }
      },
      "ProfileUpdate": {
        "type": "object",
        "description": "What a settings form submits.\n\nEvery field is optional and `None` means *leave it alone*, so a client that knows about four\nfields does not blank a fifth one it has never heard of. Clearing a field is `Some(\"\")`,\nnormalised to NULL on the way in — the alternative, a nested `Option<Option<_>>`, serialises\nto the same JSON and reads worse.",
        "properties": {
          "companyName": {
            "type": [
              "string",
              "null"
            ],
            "description": "Where they work."
          },
          "displayName": {
            "type": [
              "string",
              "null"
            ],
            "description": "What they are shown as. Rejected if blank: something has to appear in a member list."
          },
          "familyName": {
            "type": [
              "string",
              "null"
            ],
            "description": "Their surname. Editable since migration 0032."
          },
          "givenName": {
            "type": [
              "string",
              "null"
            ],
            "description": "Their first name."
          },
          "jobTitle": {
            "type": [
              "string",
              "null"
            ],
            "description": "What they do there."
          },
          "marketingOptIn": {
            "type": [
              "boolean",
              "null"
            ],
            "description": "Whether they want product mail."
          },
          "phone": {
            "type": [
              "string",
              "null"
            ],
            "description": "A number to reach them on."
          }
        }
      },
      "Provenance": {
        "type": "object",
        "description": "Where a knowledge object came from (FR-011, FR-052).\n\nOmitted relative to `domain/types.ts`, per rule 2 in the module docs: **`reviewed`**. The\nfront-end draft's own comment names it correctly — publisher review state is authoring\nmetadata and not part of the portable artifact. A registry serves packages, not projects,\nso it has nothing to read (invariant 9, PRD-0002, ADR-0005).",
        "required": [
          "knowledgeObjectId",
          "chain"
        ],
        "properties": {
          "anchor": {
            "$ref": "#/components/schemas/SourceAnchor",
            "description": "Its primary source anchor. Absent for an object with no provenance, which is\nnon-conformant (FR-011)."
          },
          "chain": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ProvenanceStep"
            },
            "description": "The full trail, in declared order, so the derivation is auditable (FR-052)."
          },
          "knowledgeObjectId": {
            "type": "string",
            "description": "The knowledge object."
          },
          "sourceDigest": {
            "type": "string",
            "description": "Digest of the source asset, for detecting drift. Absent where the package records\nnone for that asset."
          }
        }
      },
      "ProvenanceStep": {
        "type": "object",
        "description": "One link in the derivation of a knowledge object.\n\nOmitted relative to `domain/types.ts`, per rule 2 in the module docs: **`handler`** and\n**`version`**. Which extraction handler produced a chunk, at which version, is build\nlineage — authoring state in `akp-project` (invariant 9). It is deliberately not in the\nportable artifact, so a package read from a registry cannot report it.",
        "required": [
          "stage",
          "detail"
        ],
        "properties": {
          "detail": {
            "type": "string",
            "description": "Which anchor, rendered for a reader."
          },
          "stage": {
            "type": "string",
            "description": "How this object relates to the anchor: `extracted-from`, `derived-from`,\n`asserted-by`."
          }
        }
      },
      "ProviderSummary": {
        "type": "object",
        "description": "One provider a client may offer a button for.",
        "required": [
          "id",
          "displayName"
        ],
        "properties": {
          "displayName": {
            "type": "string",
            "description": "What the button should say."
          },
          "id": {
            "type": "string",
            "description": "The route segment: `/v1/auth/{id}/start`."
          }
        }
      },
      "ProvisionPlanView": {
        "type": "object",
        "description": "What a pull would transfer, and what it would decline (FR-323).",
        "required": [
          "canonicalBytes",
          "derivedBytes",
          "skippedBytes",
          "transferBytes",
          "offers",
          "selectedProfiles"
        ],
        "properties": {
          "canonicalBytes": {
            "type": "integer",
            "format": "int64",
            "description": "Bytes of canonical content. Always transferred — a package without it is not one.",
            "minimum": 0
          },
          "derivedBytes": {
            "type": "integer",
            "format": "int64",
            "description": "Bytes of derived content this selection would take.",
            "minimum": 0
          },
          "offers": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/DerivedOfferView"
            },
            "description": "Every derived artifact the release carries, chosen or not."
          },
          "selectedProfiles": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "The profiles this selection includes."
          },
          "skippedBytes": {
            "type": "integer",
            "format": "int64",
            "description": "Bytes of derived content this selection would decline.",
            "minimum": 0
          },
          "transferBytes": {
            "type": "integer",
            "format": "int64",
            "description": "What this selection would move in total.",
            "minimum": 0
          }
        }
      },
      "PublishedBundle": {
        "type": "object",
        "description": "One published release, reduced to what an operator page shows.",
        "required": [
          "packageId",
          "version",
          "visibility"
        ],
        "properties": {
          "packageId": {
            "type": "string",
            "description": "The flattened coordinate."
          },
          "publisherDated": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "The publication date the *publisher* asserted, where they asserted one.\n\nNot \"when they uploaded it to us\" — there is no such column, and migration 0007 removed\nthe default that used to invent one. This is the specification's own date, supplied by\nwhoever published, and it is absent for most bundles because most have none.\n\nNamed for what it is rather than what an operator page would like it to be. A field called\n`published_at` that is empty on nine rows in ten reads as a bug in the page."
          },
          "version": {
            "type": "string",
            "description": "The released version."
          },
          "visibility": {
            "type": "string",
            "description": "`public` or `private`."
          }
        }
      },
      "Publisher": {
        "type": "object",
        "description": "Who published a package.",
        "required": [
          "organisation"
        ],
        "properties": {
          "contact": {
            "type": "string",
            "description": "Contact address. Absent means the publisher declared none."
          },
          "namespace": {
            "type": "string",
            "description": "The registry namespace this was published under.\n\nThe namespace half of the coordinate, which is the unit of verified ownership\n(ADR-0009). Absent for a package that did not come from a registry."
          },
          "organisation": {
            "type": "string",
            "description": "Publishing organisation, as the manifest declares it."
          }
        }
      },
      "Reachability": {
        "type": "string",
        "description": "Who may reach a deployment (FR-1074, FR-1075, FR-1076).",
        "enum": [
          "account",
          "shared",
          "public"
        ]
      },
      "RefundRequest": {
        "type": "object",
        "description": "How much to give back.",
        "properties": {
          "amountMinor": {
            "type": [
              "integer",
              "null"
            ],
            "format": "int64",
            "description": "Minor units. Absent refunds everything still refundable."
          },
          "reason": {
            "type": [
              "string",
              "null"
            ],
            "description": "Why, for the provider's own records and ours."
          }
        }
      },
      "RegisterRequest": {
        "type": "object",
        "description": "What registration asks for.",
        "required": [
          "email",
          "password"
        ],
        "properties": {
          "campaign": {
            "type": [
              "string",
              "null"
            ],
            "description": "Where the intent to register came from, e.g. `homepage-hero`.\n\nOptional, untrusted, and never load-bearing. It arrives from a query parameter anybody can\ntype, it is normalised before it is stored, and a value that cannot be normalised is\ndropped rather than refused — an attribution nobody can read is a reporting gap, and a\nregistration refused because of one is a lost customer."
          },
          "displayName": {
            "type": "string",
            "description": "What to call this person. Optional; the address's local part is used if it is blank."
          },
          "email": {
            "type": "string",
            "description": "The address to register, and to prove."
          },
          "password": {
            "type": "string",
            "description": "The password. Length is the only rule (FR-681)."
          },
          "passwordConfirmation": {
            "type": [
              "string",
              "null"
            ],
            "description": "The same password again.\n\nOptional on the wire so a script or an older client is not broken by its arrival, and\nchecked when present. The form always sends it: a mistyped password on a form with no\nconfirmation is discovered at the next sign-in, by somebody who now has to reset it."
          }
        }
      },
      "ReleaseManifestView": {
        "type": "object",
        "description": "A release manifest as the wire carries it.",
        "required": [
          "schema",
          "coordinate",
          "specVersion",
          "canonicalDigest",
          "releaseDigest",
          "descriptors"
        ],
        "properties": {
          "canonicalDigest": {
            "type": "string",
            "description": "Identity over canonical content only (ADR-0003)."
          },
          "coordinate": {
            "type": "string",
            "description": "The coordinate this manifest describes."
          },
          "descriptors": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/DescriptorView"
            },
            "description": "Every constituent blob."
          },
          "releaseDigest": {
            "type": "string",
            "description": "Digest over this manifest's own naming and descriptor set."
          },
          "schema": {
            "type": "string",
            "description": "Schema identifier, so a future revision is detectable."
          },
          "specVersion": {
            "type": "string",
            "description": "Specification version the artifact declared."
          }
        }
      },
      "RequestStatus": {
        "type": "string",
        "description": "Where a request has got to.",
        "enum": [
          "open",
          "done",
          "refused"
        ]
      },
      "RescoreReport": {
        "type": "object",
        "description": "How many entries a rescore touched.",
        "required": [
          "rescored"
        ],
        "properties": {
          "rescored": {
            "type": "integer",
            "description": "The count.",
            "minimum": 0
          }
        }
      },
      "ResendReceipt": {
        "type": "object",
        "description": "Where a resent message went.",
        "required": [
          "recipient"
        ],
        "properties": {
          "recipient": {
            "type": "string",
            "description": "The address it was sent to, so the operator can see it without reopening the panel."
          }
        }
      },
      "ResetCompleteRequest": {
        "type": "object",
        "description": "A reset link and the password to set.",
        "required": [
          "token",
          "password"
        ],
        "properties": {
          "password": {
            "type": "string",
            "description": "The new password."
          },
          "token": {
            "type": "string",
            "description": "The token from the emailed link."
          }
        }
      },
      "Resolved": {
        "type": "object",
        "description": "An alias, resolved.\n\nCarries the full coordinate as well as the alias, because a client that deploys `mqtt` should\nprint what `mqtt` turned out to mean. A deployment nobody can name afterwards is one nobody can\nreproduce.",
        "required": [
          "alias",
          "package",
          "version",
          "description"
        ],
        "properties": {
          "alias": {
            "type": "string",
            "description": "The short name that was asked for."
          },
          "description": {
            "type": "string",
            "description": "What it is for."
          },
          "package": {
            "type": "string",
            "description": "The package identity, flattened as the rest of the API spells it."
          },
          "version": {
            "type": "string",
            "description": "The version it resolved to."
          }
        }
      },
      "Role": {
        "type": "string",
        "description": "What a member may do within their account (FR-1031).\n\nA ladder rather than a set, so \"at least as much as\" is a comparison and not an\nenumeration. Finer grain is a per-bundle grant (ADR-0031), which is additive and scoped to\none package — this is only about the account as a whole.",
        "enum": [
          "member",
          "publisher",
          "owner"
        ]
      },
      "RoleChange": {
        "type": "object",
        "description": "What a member's role should become.",
        "required": [
          "role"
        ],
        "properties": {
          "role": {
            "$ref": "#/components/schemas/Role",
            "description": "The new role."
          }
        }
      },
      "Section": {
        "type": "object",
        "description": "One passage of documentation, as generated.",
        "required": [
          "id",
          "topic",
          "document",
          "title",
          "source",
          "body"
        ],
        "properties": {
          "body": {
            "type": "string",
            "description": "The passage itself, verbatim."
          },
          "document": {
            "type": "string",
            "description": "Which document it came from."
          },
          "id": {
            "type": "string",
            "description": "Stable across regeneration, so a citation keeps meaning something."
          },
          "source": {
            "type": "string",
            "description": "Where a reader can go and check it. A real, published URL."
          },
          "title": {
            "type": "string",
            "description": "The heading it sits under."
          },
          "topic": {
            "type": "string",
            "description": "`specification`, `tool` or `adapters`."
          }
        }
      },
      "SessionCapabilities": {
        "type": "object",
        "description": "What a session may do (FR-800).\n\nEverything optional here follows the same rule: absent means the surface does not exist\nfor this session, never that it exists and is withheld.",
        "required": [
          "authenticated"
        ],
        "properties": {
          "account": {
            "type": "string",
            "description": "The account this session acts for, when authenticated."
          },
          "accountName": {
            "type": "string",
            "description": "The organisation's own name, where this deployment holds one.\n\nThe field above is a slug: a tenancy key, unique and stable and not a name anybody chose\nto be known by. This is the name, and `akp build` stamps it onto a package as the\npublisher — so a bundle published from an account says who published it without the\nperson having to type their own organisation's name at every build.\n\nFilled in by the handler, where the database is."
          },
          "ai": {
            "$ref": "#/components/schemas/AiCapability",
            "description": "AI capability, **absent entirely** when unavailable (ADR-0019 rule 3, FR-803)."
          },
          "analyticsShareUrl": {
            "type": "string",
            "description": "An analytics dashboard that opens without a second login, for staff only.\n\nAbsent for everybody who is not platform staff, and absent again where the deployment has\nconfigured none. Here rather than in the web bundle because it is a bearer token: a build\nvariable would put it in JavaScript any visitor can read, and an operator-only panel\nrendering a string the whole world already has is not a boundary."
          },
          "authenticated": {
            "type": "boolean",
            "description": "Whether there is an authenticated identity behind this session."
          },
          "billing": {
            "$ref": "#/components/schemas/BillingCapability",
            "description": "The subscription surface, **absent entirely** where this deployment has no billing.\n\nThe same rule again, and the case it matters most for. FR-1042 says no commercial surface\nappears in a deployment that is not the hosted one, and a client told `billing: false`\nhas been told a paid tier exists and is being withheld — which is precisely the padlock\ninvariant 13 forbids. A client told nothing renders nothing.\n\nAlso absent for an anonymous visitor. A price list is a commercial surface, and invariant\n13 keeps those away from somebody who has not signed in."
          },
          "platformAdmin": {
            "type": "boolean",
            "description": "Whether this person works on the platform (migration 0023).\n\n**Absent entirely** when false, rather than `false` — the same rule the AI surface\nfollows (ADR-0019 rule 3). A client that is told `platformAdmin: false` has been told an\noperator surface exists and is being withheld; a client told nothing renders nothing.\nFor the highest privilege in the system, absent is the right shape."
          },
          "role": {
            "$ref": "#/components/schemas/Role",
            "description": "What this identity may do in that account (FR-1031).\n\nHere so a client can learn its own reach from the one endpoint it already calls, rather\nthan being told at sign-in and then having to be told again when it changes."
          }
        }
      },
      "SettleRequest": {
        "type": "object",
        "description": "What staff decided.",
        "required": [
          "status"
        ],
        "properties": {
          "status": {
            "$ref": "#/components/schemas/RequestStatus",
            "description": "`done` or `refused`. Not `open`: a settled request is not reopened."
          }
        }
      },
      "Share": {
        "type": "object",
        "description": "One share, as an operator or publisher sees it (FR-940).",
        "required": [
          "id",
          "email",
          "mode",
          "expiresAt",
          "createdAt",
          "live"
        ],
        "properties": {
          "claimedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When it was taken up, if it has been."
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "description": "When it was created."
          },
          "createdBy": {
            "type": [
              "string",
              "null"
            ],
            "description": "Who offered it, by address (FR-903, FR-940).\n\n`None` where the maker has since been erased, or where the share predates the column being\nwritten — a share is still a fact about access when the person who made it has gone."
          },
          "email": {
            "type": "string",
            "description": "Who it was offered to."
          },
          "expiresAt": {
            "type": "string",
            "format": "date-time",
            "description": "When it stops being claimable."
          },
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "Identity, for revoking it."
          },
          "live": {
            "type": "boolean",
            "description": "Whether it can still be taken up right now — the question the list is read to answer."
          },
          "mode": {
            "$ref": "#/components/schemas/Mode",
            "description": "Which of the two modes."
          },
          "revokedAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When it was withdrawn, if it has been."
          }
        }
      },
      "ShareCreated": {
        "type": "object",
        "description": "What a share produced.",
        "required": [
          "link"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "The share, for revoking it later.\n\nAbsent for a public bundle, which records no share because it grants nothing (FR-920):\nthere is nothing to revoke, and returning an identity for a row that does not exist would\ninvite a caller to try."
          },
          "link": {
            "type": "string",
            "description": "The link that was sent, so the sharer can copy it rather than wait for the mail.\n\nFor a direct share this **contains the one-time token** and is the only time it is ever\navailable — the server keeps a digest and cannot reproduce it."
          }
        }
      },
      "ShareDeploymentRequest": {
        "type": "object",
        "description": "Giving a deployment to somebody.",
        "required": [
          "email"
        ],
        "properties": {
          "email": {
            "type": "string",
            "description": "The address to give it to."
          },
          "expiresInDays": {
            "type": [
              "integer",
              "null"
            ],
            "format": "int64",
            "description": "How long it lasts. Defaulted rather than optional-for-ever."
          }
        }
      },
      "ShareRequest": {
        "type": "object",
        "description": "Share a bundle with somebody (FR-900, FR-910, FR-912, FR-920).",
        "required": [
          "email",
          "mode"
        ],
        "properties": {
          "days": {
            "type": [
              "integer",
              "null"
            ],
            "format": "int64",
            "description": "How long it stays claimable. Defaults to a fortnight, capped at ninety days."
          },
          "email": {
            "type": "string",
            "description": "Who to send it to."
          },
          "mode": {
            "$ref": "#/components/schemas/Mode",
            "description": "How they are meant to get it. Ignored for a public bundle, which grants nothing."
          }
        }
      },
      "SignatureDetail": {
        "type": "object",
        "description": "A signature as the publisher made it.\n\nDeliberately carries no verdict. Whether this key is *trustworthy* is the reader's decision\nand never the registry's — reporting one would be the vendor root of trust the format exists\nto avoid (ADR-0013). What is reported is what was signed and by which key.",
        "required": [
          "algorithm",
          "publicKey",
          "canonicalDigest",
          "package",
          "signature"
        ],
        "properties": {
          "algorithm": {
            "type": "string",
            "description": "Signature algorithm. Only `ed25519` is defined at specification version 0.1."
          },
          "canonicalDigest": {
            "type": "string",
            "description": "The canonical digest the signature covers.\n\nRestated so a reader can see *what* was signed without recomputing it — and so a\nsignature over a different package is visibly a signature over a different package."
          },
          "package": {
            "type": "string",
            "description": "The package identity the signature names."
          },
          "publicKey": {
            "type": "string",
            "description": "The signer's public key, hex encoded. This is what a verifier checks against."
          },
          "signature": {
            "type": "string",
            "description": "The signature bytes, hex encoded."
          }
        }
      },
      "SourceAnchor": {
        "type": "object",
        "description": "An asset plus a location within it.\n\nOmitted relative to `domain/types.ts`, per rule 2 in the module docs: **`excerpt`**.\nReturning the verbatim source text at an anchor means re-running the extraction handler\nfor the asset's media type over its bytes and slicing at the locator. That is an\n`akp_core::ingest` operation with no read-path equivalent — the core exposes no\n\"text at this locator\" function — so there is nothing here to call. It is the largest\nknown gap in this route and it is a core capability, not a transport one.",
        "required": [
          "assetId",
          "assetPath",
          "mediaType",
          "locator",
          "display"
        ],
        "properties": {
          "assetId": {
            "type": "string",
            "description": "The asset the content came from."
          },
          "assetPath": {
            "type": "string",
            "description": "That asset's path."
          },
          "display": {
            "type": "string",
            "description": "Human-readable rendering, e.g. `manual.pdf @ page 147`."
          },
          "locator": {
            "$ref": "#/components/schemas/SourceLocator",
            "description": "Where within the asset."
          },
          "mediaType": {
            "type": "string",
            "description": "The asset's media type."
          }
        }
      },
      "SourceLocator": {
        "oneOf": [
          {
            "type": "object",
            "description": "A page of a paginated document.",
            "required": [
              "page",
              "kind"
            ],
            "properties": {
              "kind": {
                "type": "string",
                "enum": [
                  "page"
                ]
              },
              "page": {
                "type": "integer",
                "format": "int32",
                "description": "One-based page number.",
                "minimum": 0
              }
            }
          },
          {
            "type": "object",
            "description": "A range of lines in a text document.",
            "required": [
              "start",
              "end",
              "kind"
            ],
            "properties": {
              "end": {
                "type": "integer",
                "format": "int32",
                "description": "One-based inclusive end line.",
                "minimum": 0
              },
              "kind": {
                "type": "string",
                "enum": [
                  "lines"
                ]
              },
              "start": {
                "type": "integer",
                "format": "int32",
                "description": "One-based inclusive start line.",
                "minimum": 0
              }
            }
          },
          {
            "type": "object",
            "description": "A named section, as a heading path from the document root.",
            "required": [
              "path",
              "kind"
            ],
            "properties": {
              "kind": {
                "type": "string",
                "enum": [
                  "section"
                ]
              },
              "path": {
                "type": "array",
                "items": {
                  "type": "string"
                },
                "description": "Heading path, outermost first."
              }
            }
          },
          {
            "type": "object",
            "description": "A cell range within a named sheet.",
            "required": [
              "name",
              "range",
              "kind"
            ],
            "properties": {
              "kind": {
                "type": "string",
                "enum": [
                  "sheet"
                ]
              },
              "name": {
                "type": "string",
                "description": "Sheet name."
              },
              "range": {
                "type": "string",
                "description": "A1-style range, e.g. `B2:D40`."
              }
            }
          },
          {
            "type": "object",
            "description": "A byte range in the source.",
            "required": [
              "start",
              "end",
              "kind"
            ],
            "properties": {
              "end": {
                "type": "integer",
                "format": "int64",
                "description": "Exclusive end offset.",
                "minimum": 0
              },
              "kind": {
                "type": "string",
                "enum": [
                  "byte-range"
                ]
              },
              "start": {
                "type": "integer",
                "format": "int64",
                "description": "Inclusive start offset.",
                "minimum": 0
              }
            }
          }
        ],
        "description": "A location within a source asset (FR-011).\n\nClosed by locator kind so a renderer cannot silently ignore a form of anchor it has not\nhandled. The variants mirror `akp_core::model::Locator` exactly, which is why `lines`\nappears where the front-end draft had `slide`: the core defines the former and not the\nlatter, and the specification is what a wire type has to agree with."
      },
      "SpeakRequest": {
        "type": "object",
        "description": "What to say.",
        "required": [
          "text"
        ],
        "properties": {
          "text": {
            "type": "string",
            "description": "The words. Already stripped of Markdown by the client, which renders the same text."
          }
        }
      },
      "StandingRequest": {
        "type": "object",
        "description": "Restrict an account, or lift a restriction (ADR-0046).\n\nOne column, two consequences: nobody may sign in, and the account's public packages stop being\nserved to anybody else. Both are read where they matter rather than enforced here.\n\nReversible on purpose. Nothing is deleted and nothing is anonymised, so lifting a restriction\nrestores the account exactly — which is what makes it usable during a dispute rather than only\nat the end of one.",
        "required": [
          "restricted"
        ],
        "properties": {
          "restricted": {
            "type": "boolean",
            "description": "`true` to restrict, `false` to lift it."
          }
        }
      },
      "StartResponse": {
        "type": "object",
        "description": "Where to send the browser to sign in.",
        "required": [
          "authorizationUrl"
        ],
        "properties": {
          "authorizationUrl": {
            "type": "string",
            "description": "The provider's authorisation endpoint, fully parameterised with PKCE, state and nonce."
          }
        }
      },
      "Status": {
        "type": "string",
        "description": "Where an entry is in the pipeline.\n\nFour values and no more. A status vocabulary grows one plausible addition at a time until\nnobody can say what the list is filtered by; these four answer \"does this need my attention\",\nwhich is the only question the queue exists to answer.",
        "enum": [
          "new",
          "contacted",
          "processed",
          "not-interested",
          "lost"
        ]
      },
      "SubscriptionDirectory": {
        "type": "object",
        "description": "The cross-tenant list, with the one number worth putting at the top.",
        "required": [
          "entries",
          "monthlyRecurringMinor",
          "currency"
        ],
        "properties": {
          "currency": {
            "type": "string",
            "description": "The currency that figure is in.\n\nTaken from the first entry rather than summed across currencies, and reported so a page\ncannot render a total in the wrong one. Mixing currencies in a single sum is a defect this\nproduct does not have yet and would rather notice than average."
          },
          "entries": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/DirectoryEntry"
            },
            "description": "Every account with a subscription."
          },
          "monthlyRecurringMinor": {
            "type": "integer",
            "format": "int64",
            "description": "Monthly recurring revenue in minor units, counting only what recurs."
          }
        }
      },
      "SubscriptionRecord": {
        "type": "object",
        "description": "One account's subscription.",
        "required": [
          "id",
          "provider",
          "plan",
          "status",
          "cancelAtPeriodEnd",
          "createdAt",
          "manageable"
        ],
        "properties": {
          "cancelAtPeriodEnd": {
            "type": "boolean",
            "description": "Cancelled, but running to the end of the period."
          },
          "canceledAt": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When it ended, if it has."
          },
          "createdAt": {
            "type": "string",
            "format": "date-time",
            "description": "When it started."
          },
          "currentPeriodEnd": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "End of the paid period. What a cancellation runs to."
          },
          "currentPeriodStart": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "Start of the paid period."
          },
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "Our identifier."
          },
          "manageable": {
            "type": "boolean",
            "description": "Whether the provider can be asked to change it.\n\nFalse for a manual grant, which has no provider object behind it. Reported rather than\ninferred so a client never draws a cancel button that cannot work."
          },
          "plan": {
            "$ref": "#/components/schemas/Plan",
            "description": "The plan it is for."
          },
          "provider": {
            "type": "string",
            "description": "`stripe` or `manual`."
          },
          "status": {
            "$ref": "#/components/schemas/SubscriptionStatus",
            "description": "Where it stands."
          }
        }
      },
      "SubscriptionStatus": {
        "type": "string",
        "description": "Where a subscription stands, in the provider's own vocabulary.\n\nStripe's words rather than ours. Translating at the boundary would mean deciding what\n`incomplete_expired` means to this product inside a webhook handler at whatever hour it\narrives; storing what we were told and interpreting it in [`Self::entitles`] keeps that\ndecision in one reviewable place.\n\n## Two spellings, and which is which\n\n[`Self::as_str`] and [`FromStr`] use the provider's spelling — `past_due` — because that is\nwhat arrives in a webhook and what the `CHECK` constraint in migration 0026 permits. Serde\nrenames to kebab-case, so the JSON a client sees is `past-due`, matching every other enum on\nthis API.\n\nDeliberate, and worth stating because it looks like a bug the first time somebody greps for\none spelling and finds the other. The database and the provider agree with each other; the\nwire agrees with the rest of the wire. Nothing converts by hand: the two paths are `as_str`\nand `serde`, and neither is ever used for the other's job.",
        "enum": [
          "trialing",
          "active",
          "past-due",
          "canceled",
          "incomplete",
          "incomplete-expired",
          "unpaid",
          "paused"
        ]
      },
      "SubscriptionView": {
        "type": "object",
        "description": "Everything the subscription screen renders, in one response.\n\nOne call rather than four. The screen needs the plan, the subscription, the payments and the\nhistory together, and four requests would render in four stages with the page moving under the\nreader each time.",
        "required": [
          "plan",
          "payments",
          "history",
          "purchasable"
        ],
        "properties": {
          "history": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/BillingEvent"
            },
            "description": "What has happened, newest first."
          },
          "payments": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PaymentRecord"
            },
            "description": "What has been paid, newest first."
          },
          "plan": {
            "$ref": "#/components/schemas/PlanRecord",
            "description": "The plan it is on, or the plan the account is on without one."
          },
          "purchasable": {
            "type": "boolean",
            "description": "Whether anything can actually be bought on this deployment.\n\nReported so the client never draws a buy button that cannot work — a deployment with\nprovider keys and no configured price is one where checkout fails at the last step."
          },
          "subscription": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/SubscriptionRecord",
                "description": "The live subscription, where there is one."
              }
            ]
          }
        }
      },
      "SupportReceipt": {
        "type": "object",
        "description": "What to quote when chasing it.",
        "required": [
          "reference"
        ],
        "properties": {
          "reference": {
            "type": "string",
            "description": "The reference, `SR-000123`."
          }
        }
      },
      "SupportRequest": {
        "type": "object",
        "description": "A question for support.",
        "required": [
          "subject",
          "body"
        ],
        "properties": {
          "body": {
            "type": "string",
            "description": "The problem."
          },
          "subject": {
            "type": "string",
            "description": "One line naming the problem."
          }
        }
      },
      "Surface": {
        "type": "string",
        "description": "Which conversation this is (PRD-0009 §6.2, §6.3).\n\nTwo surfaces, one transport. They differ in the tools the graph is given and in the system\nframing, not in the plumbing, so a single client component serves both (ADR-0014's shared\ncomponent library rule).",
        "enum": [
          "discovery",
          "authoring"
        ]
      },
      "TurnRequest": {
        "type": "object",
        "description": "A request to advance a conversation (FR-804).\n\nThere is no endpoint that starts a conversation on a timer, on project open, or on file\nchange. A turn happens because a person sent a message, which is what keeps the\nlocal-first invariant intact with an assistant in the product.",
        "required": [
          "surface",
          "message"
        ],
        "properties": {
          "conversation": {
            "type": "string",
            "description": "The conversation to continue, or `None` to begin one."
          },
          "disclosedContent": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Content the user has agreed to send (FR-805).\n\nThe client states what will be transmitted and the user accepts before this is\npopulated. An empty list is a conversation about nothing but the conversation, which\nis the correct default."
          },
          "location": {
            "$ref": "#/components/schemas/PageContext",
            "description": "What the user is looking at while they ask."
          },
          "message": {
            "type": "string",
            "description": "What the user said."
          },
          "project": {
            "type": "string",
            "description": "The project this concerns, for the authoring surface."
          },
          "surface": {
            "$ref": "#/components/schemas/Surface",
            "description": "Which surface."
          }
        }
      },
      "TurnResponse": {
        "type": "object",
        "description": "The wire response for a turn.",
        "required": [
          "conversation",
          "reply"
        ],
        "properties": {
          "budgetRemainingTokens": {
            "type": "integer",
            "format": "int64",
            "description": "Budget left after this turn, where the account is metered (FR-849).",
            "minimum": 0
          },
          "citations": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Citation"
            },
            "description": "Citations backing the reply. Always serialised, for the reason above."
          },
          "conversation": {
            "type": "string",
            "description": "The conversation identifier."
          },
          "proposals": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AgentProposal"
            },
            "description": "Proposals produced, awaiting review.\n\nAlways serialised, empty included. The schema declares it a required array, and a client\nthat trusts the schema and spreads what arrives gets `undefined` when the field is\nomitted — which is a crash, not a missing section."
          },
          "reply": {
            "type": "string",
            "description": "What the agent said."
          }
        }
      },
      "ValidateRequest": {
        "type": "object",
        "description": "A package submitted for validation.",
        "required": [
          "package"
        ],
        "properties": {
          "package": {
            "type": "object",
            "description": "The package, as its canonical JSON form.\n\nDescribed to `OpenAPI` as an opaque object rather than as a generated schema.\n`akp-core` carries no `utoipa` dependency — invariant 3 and ADR-0002 keep the crate\nfree of transport concerns, and a derive macro for an HTTP framework's documentation\ngenerator is exactly such a concern. The normative schema for a package is the\ninformation model in `docs/spec/`, not an artifact of this crate's annotations.",
            "additionalProperties": {},
            "propertyNames": {
              "type": "string"
            }
          }
        }
      },
      "ValidationCheck": {
        "type": "object",
        "description": "A single conformance check.",
        "required": [
          "id",
          "requirement",
          "title",
          "outcome",
          "detail"
        ],
        "properties": {
          "detail": {
            "type": "string",
            "description": "What was found, in terms a user can act on."
          },
          "id": {
            "type": "string",
            "description": "Stable identity within this report, so a client can key a list on it."
          },
          "outcome": {
            "$ref": "#/components/schemas/CheckOutcome",
            "description": "What happened."
          },
          "requirement": {
            "type": "string",
            "description": "The requirement this enforces, e.g. `FR-051`. Traceability is not optional."
          },
          "subject": {
            "type": "string",
            "description": "The object concerned, where the finding names one."
          },
          "title": {
            "type": "string",
            "description": "Short label for the requirement."
          }
        }
      },
      "ValidationReport": {
        "type": "object",
        "description": "A conformance report (FR-053).",
        "required": [
          "packageId",
          "version",
          "specificationVersion",
          "profiles",
          "status",
          "sections",
          "generatedAt"
        ],
        "properties": {
          "generatedAt": {
            "type": "string",
            "description": "When this report was produced, RFC 3339."
          },
          "packageId": {
            "type": "string",
            "description": "Package identity."
          },
          "profiles": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Conformance profiles satisfied, highest last (FR-055)."
          },
          "sections": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ValidationSection"
            },
            "description": "Structural and knowledge-quality sections."
          },
          "specificationVersion": {
            "type": "string",
            "description": "Specification version the package declares."
          },
          "status": {
            "$ref": "#/components/schemas/ValidationStatus",
            "description": "Overall status."
          },
          "version": {
            "type": "string",
            "description": "The version validated."
          }
        }
      },
      "ValidationSection": {
        "type": "object",
        "description": "A group of checks that answer the same question.\n\nFR-044: structural validity and knowledge quality are reported separately because they\nanswer different questions. A package can be perfectly valid and still perform badly on a\ngiven AI stack; conflating the two would make the conformance report a quality score,\nwhich it is not.",
        "required": [
          "id",
          "title",
          "description",
          "gatesValidity",
          "outcome",
          "checks"
        ],
        "properties": {
          "checks": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ValidationCheck"
            },
            "description": "The checks."
          },
          "description": {
            "type": "string",
            "description": "What this section is for."
          },
          "gatesValidity": {
            "type": "boolean",
            "description": "Whether findings here can make the package invalid, or are advisory only."
          },
          "id": {
            "type": "string",
            "description": "Section identity: `structural` or `knowledge-quality`."
          },
          "outcome": {
            "$ref": "#/components/schemas/CheckOutcome",
            "description": "The section's overall outcome — its worst check."
          },
          "title": {
            "type": "string",
            "description": "Section title."
          }
        }
      },
      "ValidationStatus": {
        "type": "string",
        "description": "Overall conformance outcome (FR-053, FR-055).\n\n`ValidWithAdvisories` is load-bearing, not a nicety. FR-044 separates package validity\nfrom runtime quality, so an advisory finding — a stale embedding profile, an external\nasset that does not say how to resolve itself — must not be able to make a structurally\nsound package read as broken.",
        "enum": [
          "valid",
          "valid-with-advisories",
          "invalid",
          "unvalidated"
        ]
      },
      "VerificationView": {
        "type": "object",
        "description": "The result of running a check (FR-1045).",
        "required": [
          "claim",
          "verified",
          "recordsSeen",
          "detail"
        ],
        "properties": {
          "claim": {
            "$ref": "#/components/schemas/NamespaceClaimView",
            "description": "The claim as it now stands."
          },
          "detail": {
            "type": "string",
            "description": "What to say to the person who just added a record."
          },
          "recordsSeen": {
            "type": "integer",
            "description": "How many TXT records were present at the challenge name.\n\nThe difference between \"nothing published yet\" and \"something published that does not\nmatch\" is the difference between waiting and fixing, and only the caller can tell which\nthey are looking at.",
            "minimum": 0
          },
          "verified": {
            "type": "boolean",
            "description": "Whether the record was found."
          }
        }
      },
      "VerifyRequest": {
        "type": "object",
        "description": "A verification token.",
        "required": [
          "token"
        ],
        "properties": {
          "token": {
            "type": "string",
            "description": "The token from the emailed link."
          }
        }
      }
    }
  }
}
