{
  "openapi": "3.1.0",
  "info": {
    "title": "Somebody",
    "version": "0.8.1",
    "summary": "A human-backed agent credential any service can accept instead of blocking bots at the edge.",
    "description": "Somebody lets a service accept an agent backed by a privately verified human account. The service receives a service-specific key, verification results, and (under an auth type it configured) consented identity facts — never the account's identity. This document covers the SERVICE-facing and AGENT-facing HTTP surface only: the human-facing HTML pages (/account, /approve/{id}, /how-it-works, /privacy, /services, /terms, /waitlist) are not APIs and are not described here. Source of truth: docs/contract/api.md.\n",
    "contact": {
      "url": "https://somebody.name/services"
    }
  },
  "servers": [
    {
      "url": "https://somebody.name"
    }
  ],
  "security": [],
  "tags": [
    {
      "name": "open",
      "description": "No credential required."
    },
    {
      "name": "service",
      "description": "Routes authenticated with a registered service's secret (sb_s_<40 hex>)."
    },
    {
      "name": "grant",
      "description": "Routes authenticated with a human-approved grant's minting token (sb_g_<40 hex>)."
    },
    {
      "name": "agent-onramp",
      "description": "The agent's path to a key — no credential until a grant token exists."
    },
    {
      "name": "session",
      "description": "Routes authenticated with the human's session cookie (set by signing in at /account or /approve/{id})."
    },
    {
      "name": "oauth",
      "description": "v0.5.2: Sign in with Somebody — Somebody as the OAuth 2.1 authorization server for a service's own MCP server or app (plan v3 §3 G)."
    }
  ],
  "paths": {
    "/health": {
      "get": {
        "operationId": "health",
        "tags": [
          "open"
        ],
        "summary": "Liveness and store reachability.",
        "security": [],
        "responses": {
          "200": {
            "description": "The issuer is up and can reach its store.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "ok",
                    "store",
                    "now"
                  ],
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "const": true
                    },
                    "store": {
                      "type": "string",
                      "const": "ok"
                    },
                    "now": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/version": {
      "get": {
        "operationId": "version",
        "tags": [
          "open"
        ],
        "summary": "Build identity of the running deployment.",
        "security": [],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "git_sha": {
                      "type": "string"
                    },
                    "build_time": {
                      "type": "string"
                    },
                    "environment": {
                      "type": "string",
                      "enum": [
                        "preview",
                        "production",
                        "test"
                      ]
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/service": {
      "get": {
        "operationId": "getService",
        "tags": [
          "service"
        ],
        "summary": "Own configuration.",
        "security": [
          {
            "serviceSecret": []
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "service",
                    "phone_policy"
                  ],
                  "properties": {
                    "service": {
                      "type": "object",
                      "required": [
                        "slug",
                        "name",
                        "requirements",
                        "actions",
                        "default_enforcement",
                        "key_cap",
                        "key_lifetime_seconds"
                      ],
                      "properties": {
                        "slug": {
                          "type": "string"
                        },
                        "name": {
                          "type": "string"
                        },
                        "requirements": {
                          "type": "object",
                          "additionalProperties": {
                            "type": "boolean"
                          },
                          "description": "Which verification facts this service requires (email, mobile, phone_number_country)."
                        },
                        "actions": {
                          "type": "object",
                          "description": "Configured action → {bucket, cost, scope}, server-side only; never supplied by the agent.",
                          "additionalProperties": {
                            "type": "object",
                            "properties": {
                              "bucket": {
                                "type": "string"
                              },
                              "cost": {
                                "type": "integer"
                              },
                              "scope": {
                                "type": "string"
                              }
                            }
                          }
                        },
                        "default_enforcement": {
                          "$ref": "#/components/schemas/Enforcement"
                        },
                        "key_cap": {
                          "type": "integer",
                          "description": "N in the N-key rotation rule: per connection since v0.7 (per account × service for a grant without a connection)."
                        },
                        "key_lifetime_seconds": {
                          "type": "integer"
                        },
                        "auth_types": {
                          "type": "array",
                          "description": "v0.4 — the service's registered auth types (consent screens), if any.",
                          "items": {
                            "$ref": "#/components/schemas/AuthType"
                          }
                        },
                        "report_categories": {
                          "type": "array",
                          "description": "v0.5.1 — the category codes POST /v1/reports accepts.",
                          "items": {
                            "type": "string",
                            "enum": [
                              "spam",
                              "scraping",
                              "vandalism",
                              "harassment",
                              "impersonation",
                              "fraud",
                              "malicious_content",
                              "illegal_content",
                              "copyright",
                              "misinformation",
                              "abuse_of_access",
                              "tos_violation",
                              "other"
                            ]
                          }
                        }
                      }
                    },
                    "phone_policy": {
                      "$ref": "#/components/schemas/PhonePolicy"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/v1/access/check": {
      "post": {
        "operationId": "accessCheck",
        "tags": [
          "service"
        ],
        "summary": "Check and consume allowance for one protected action.",
        "description": "A refused request charges nothing. Idempotency-Key replays the stored decision with replayed:true, charged once; the same key with a different body is a 409.\n",
        "security": [
          {
            "serviceSecret": []
          }
        ],
        "parameters": [
          {
            "in": "header",
            "name": "Idempotency-Key",
            "required": false,
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 128
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "key",
                  "action"
                ],
                "properties": {
                  "key": {
                    "type": "string",
                    "pattern": "^sb_k_[0-9a-f]{40}$"
                  },
                  "action": {
                    "type": "string",
                    "pattern": "^[a-z][a-z0-9_]{0,63}$",
                    "description": "Must be one of this service's configured actions."
                  },
                  "request_ref": {
                    "type": "string",
                    "maxLength": 200,
                    "description": "An id, never a body — see docs/contract/api.md \"What NoHumans must do\"."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "A decision, always 200 whether allow or deny.",
            "headers": {
              "Retry-After": {
                "description": "Present when retry_after is non-null.",
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CheckResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "422": {
            "$ref": "#/components/responses/InvalidBody"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/v1/access/rules": {
      "post": {
        "operationId": "accessRules",
        "tags": [
          "service"
        ],
        "summary": "The effective rules for the agent, without consuming allowance (cost 0).",
        "description": "Same body as /v1/access/check; no Idempotency-Key; consumes nothing. Use after authorization, to publish rules to the agent.",
        "security": [
          {
            "serviceSecret": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "key",
                  "action"
                ],
                "properties": {
                  "key": {
                    "type": "string",
                    "pattern": "^sb_k_[0-9a-f]{40}$"
                  },
                  "action": {
                    "type": "string",
                    "pattern": "^[a-z][a-z0-9_]{0,63}$"
                  },
                  "request_ref": {
                    "type": "string",
                    "maxLength": 200
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The same shape as /v1/access/check, with cost 0 and nothing consumed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CheckResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "422": {
            "$ref": "#/components/responses/InvalidBody"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/v1/policy": {
      "post": {
        "operationId": "updatePolicy",
        "tags": [
          "service"
        ],
        "summary": "Attach opaque policy data and/or enforceable limits/blocks to an account × service relationship.",
        "description": "Exactly one of key | credential_id | subject identifies the relationship. At least one of policy / enforcement is required. expected_revision guards concurrent updates: a stale value is a 409 with details.current_revision. Never touches usage counters.\n",
        "security": [
          {
            "serviceSecret": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "key": {
                    "type": "string",
                    "pattern": "^sb_k_[0-9a-f]{40}$"
                  },
                  "credential_id": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "subject": {
                    "type": "string",
                    "pattern": "^sub_[0-9a-f]{32}$"
                  },
                  "policy": {
                    "type": "object",
                    "description": "Opaque to Somebody; the service's own vocabulary (e.g. {\"nh_rate\":\"NH_HALF_RATE\"})."
                  },
                  "enforcement": {
                    "$ref": "#/components/schemas/Enforcement"
                  },
                  "expected_revision": {
                    "type": "integer",
                    "minimum": 0
                  }
                },
                "description": "Exactly one of key, credential_id, subject; at least one of policy, enforcement."
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "policy_revision",
                    "policy",
                    "enforcement",
                    "effective_enforcement"
                  ],
                  "properties": {
                    "policy_revision": {
                      "type": "integer"
                    },
                    "policy": {
                      "type": "object"
                    },
                    "enforcement": {
                      "$ref": "#/components/schemas/Enforcement"
                    },
                    "effective_enforcement": {
                      "$ref": "#/components/schemas/Enforcement"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "Stale expected_revision, or another conflict.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "$ref": "#/components/responses/InvalidBody"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/v1/reports": {
      "post": {
        "operationId": "createReport",
        "tags": [
          "service"
        ],
        "summary": "File an attributed allegation against an account × service relationship.",
        "description": "Exactly one of key | credential_id | subject, so a service can report after the key left its hands. A report changes no decision by itself — it never blocks on its own; a block is a separate POST /v1/policy call. v0.6: the human is emailed once per report (nothing returns to the service), the agent sees it in GET /v1/me until the human marks it handled, and a check denied for a block appends the newest open report's category and the appeal path to `remedy`.\n",
        "security": [
          {
            "serviceSecret": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "category"
                ],
                "properties": {
                  "key": {
                    "type": "string",
                    "pattern": "^sb_k_[0-9a-f]{40}$"
                  },
                  "credential_id": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "subject": {
                    "type": "string",
                    "pattern": "^sub_[0-9a-f]{32}$"
                  },
                  "category": {
                    "type": "string",
                    "enum": [
                      "spam",
                      "scraping",
                      "vandalism",
                      "harassment",
                      "impersonation",
                      "fraud",
                      "malicious_content",
                      "illegal_content",
                      "copyright",
                      "misinformation",
                      "abuse_of_access",
                      "tos_violation",
                      "other"
                    ],
                    "description": "v0.5.1: spam and damage. GET /v1/service lists the codes as report_categories; an unknown code is 422 naming the list."
                  },
                  "body": {
                    "type": "object",
                    "description": "At most 16 KiB. With category \"other\", body.note (string, 1–500 chars) is required, else 422."
                  },
                  "request_ref": {
                    "type": "string",
                    "maxLength": 200
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "report_id",
                    "category",
                    "status",
                    "credential_id",
                    "created_at"
                  ],
                  "properties": {
                    "report_id": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "category": {
                      "type": "string",
                      "enum": [
                        "spam",
                        "scraping",
                        "vandalism",
                        "harassment",
                        "impersonation",
                        "fraud",
                        "malicious_content",
                        "illegal_content",
                        "copyright",
                        "misinformation",
                        "abuse_of_access",
                        "tos_violation",
                        "other"
                      ]
                    },
                    "status": {
                      "type": "string",
                      "const": "open"
                    },
                    "credential_id": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "created_at": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "422": {
            "$ref": "#/components/responses/InvalidBody"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      },
      "get": {
        "operationId": "listReports",
        "tags": [
          "service"
        ],
        "summary": "Own reports only, newest first.",
        "security": [
          {
            "serviceSecret": []
          }
        ],
        "parameters": [
          {
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 50
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "reports"
                  ],
                  "properties": {
                    "reports": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "report_id": {
                            "type": "string",
                            "format": "uuid"
                          },
                          "credential_id": {
                            "type": "string",
                            "format": "uuid"
                          },
                          "category": {
                            "type": "string",
                            "enum": [
                              "spam",
                              "scraping",
                              "vandalism",
                              "harassment",
                              "impersonation",
                              "fraud",
                              "malicious_content",
                              "illegal_content",
                              "copyright",
                              "misinformation",
                              "abuse_of_access",
                              "tos_violation",
                              "other"
                            ]
                          },
                          "status": {
                            "type": "string",
                            "enum": [
                              "open",
                              "corrected",
                              "withdrawn",
                              "closed",
                              "acknowledged"
                            ],
                            "description": "v0.6 adds acknowledged: the human marked it handled (it changes nothing else)."
                          },
                          "created_at": {
                            "type": "string",
                            "format": "date-time"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/v1/authorize": {
      "post": {
        "operationId": "createAuthorization",
        "tags": [
          "agent-onramp"
        ],
        "summary": "Start a human-approval request for an agent (anonymous) or on behalf of an agent (service-initiated).",
        "description": "Anonymous form: no Authorization header; the agent's client supplies service, scopes, code_challenge and holds the PKCE verifier itself. Service-initiated form (v0.4): Authorization is the service's own secret (sb_s_<40 hex>); the service may add client_ref (echoed everywhere) and policy_seed (merged into the account × service policy at approval, before any key exists). An anonymous caller sending client_ref or policy_seed gets 403. Limit: 20 requests per hour per IP.\n",
        "security": [
          {},
          {
            "serviceSecret": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "scopes",
                  "code_challenge"
                ],
                "properties": {
                  "service": {
                    "type": "string",
                    "pattern": "^[a-z0-9][a-z0-9-]{1,62}$",
                    "description": "Required unless the caller is a service (then it must equal that service, or be omitted)."
                  },
                  "auth_type": {
                    "type": "string",
                    "pattern": "^[a-z][a-z0-9_-]{1,31}$",
                    "description": "Required when the service has more than one auth type; defaults when it has exactly one; absent means legacy (no identity block)."
                  },
                  "scopes": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "minItems": 1,
                    "maxItems": 16,
                    "description": "Must be scopes the service (or the named auth type) offers."
                  },
                  "code_challenge": {
                    "type": "string",
                    "pattern": "^[A-Za-z0-9_-]{43}$",
                    "description": "base64url(sha256(code_verifier))."
                  },
                  "code_challenge_method": {
                    "type": "string",
                    "const": "S256"
                  },
                  "client_name": {
                    "type": "string",
                    "maxLength": 80
                  },
                  "client_ref": {
                    "type": "string",
                    "maxLength": 120,
                    "description": "Service-initiated only; 403 for an anonymous caller."
                  },
                  "policy_seed": {
                    "type": "object",
                    "description": "Service-initiated only; at most 4 KiB; merged into policy at approval, revision +1."
                  },
                  "enrollment": {
                    "type": "string",
                    "pattern": "^sb_e_[0-9a-f]{40}$",
                    "description": "If it is within the enrollment's bounds, this request is approved immediately."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "request_id",
                    "service",
                    "scopes",
                    "expires_at",
                    "approval_url",
                    "poll",
                    "human_instructions"
                  ],
                  "properties": {
                    "request_id": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "service": {
                      "type": "string"
                    },
                    "auth_type": {
                      "type": "string",
                      "nullable": true
                    },
                    "client_ref": {
                      "type": "string",
                      "nullable": true
                    },
                    "scopes": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "expires_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "approval_url": {
                      "type": "string",
                      "format": "uri"
                    },
                    "status_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "v0.4 — GET this with the service secret to poll status without delivery."
                    },
                    "auto_approved": {
                      "type": "boolean",
                      "description": "Present only when \"enrollment\" was sent. When true, auth_type is the type GRANTED: a profile-wide connection (v0.5) may grant a narrower type than asked, never a wider one."
                    },
                    "not_auto_approved_because": {
                      "type": "string",
                      "description": "A subset of RefusalReason. v0.5 adds service_offers_no_anonymous_type and profile_asks_first (a profile-wide connection at a service it has not used, under the profile's new-services setting); v0.6 connection_daily_rate. v0.6: remedy and docs accompany it.",
                      "enum": [
                        "service_not_enrolled",
                        "auth_type_not_enrolled",
                        "enrollment_unknown_or_revoked",
                        "enrollment_exhausted",
                        "type_needs_human_input",
                        "real_name_missing",
                        "verification_missing",
                        "country_not_allowed",
                        "profile_archived",
                        "service_has_no_auth_type",
                        "handle_missing",
                        "handle_reserved_at_service",
                        "service_offers_no_anonymous_type",
                        "profile_asks_first",
                        "connection_daily_rate"
                      ]
                    },
                    "poll": {
                      "type": "object",
                      "properties": {
                        "method": {
                          "type": "string",
                          "const": "POST"
                        },
                        "url": {
                          "type": "string",
                          "format": "uri"
                        },
                        "body": {
                          "type": "object"
                        },
                        "interval_seconds": {
                          "type": "integer"
                        }
                      }
                    },
                    "human_instructions": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "A service sent client_ref or policy_seed without authenticating as a service, or authenticated as a different service than it named.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "$ref": "#/components/responses/InvalidBody"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/v1/authorize/{request_id}": {
      "get": {
        "operationId": "authorizationStatus",
        "tags": [
          "service"
        ],
        "summary": "Service-authenticated status poll. No delivery.",
        "security": [
          {
            "serviceSecret": []
          }
        ],
        "parameters": [
          {
            "in": "path",
            "name": "request_id",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "request_id",
                    "status"
                  ],
                  "properties": {
                    "request_id": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "pending",
                        "approved",
                        "rejected",
                        "expired",
                        "exchanged"
                      ]
                    },
                    "auth_type": {
                      "type": "string",
                      "nullable": true
                    },
                    "client_ref": {
                      "type": "string",
                      "nullable": true
                    },
                    "expires_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "decided_at": {
                      "type": "string",
                      "format": "date-time",
                      "nullable": true
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "No such request for this service.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/authorize/exchange": {
      "post": {
        "operationId": "exchangeAuthorization",
        "tags": [
          "agent-onramp"
        ],
        "summary": "Poll an authorization request; deliver the grant token exactly once, to the holder of the PKCE verifier.",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "request_id",
                  "code_verifier"
                ],
                "properties": {
                  "request_id": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "code_verifier": {
                    "type": "string",
                    "minLength": 43,
                    "maxLength": 128,
                    "description": "Unreserved characters; its S256 hash must equal the stored code_challenge."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Approved — delivered. A second call for the same request returns 410.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "status",
                    "request_id",
                    "grant_token",
                    "grant_id",
                    "service",
                    "scopes",
                    "expires_at",
                    "next"
                  ],
                  "properties": {
                    "status": {
                      "type": "string",
                      "const": "approved"
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "grant_token": {
                      "type": "string",
                      "pattern": "^sb_g_[0-9a-f]{40}$",
                      "description": "Exists only in this response; never stored, never shown again."
                    },
                    "grant_id": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "service": {
                      "type": "string"
                    },
                    "scopes": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "expires_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "auth_type": {
                      "type": "string",
                      "nullable": true
                    },
                    "subject": {
                      "type": "string",
                      "pattern": "^sub_[0-9a-f]{32}$",
                      "nullable": true
                    },
                    "client_ref": {
                      "type": "string",
                      "nullable": true
                    },
                    "auto_approved": {
                      "type": "boolean"
                    },
                    "enrollment": {
                      "type": "string",
                      "pattern": "^sb_e_[0-9a-f]{40}$",
                      "description": "v0.5 (plan v3 §2b): present once, on the exchange of a request a human approved by hand (typed, agent-initiated): the credential of the profile-wide connection that approval created. Never on the approval page, never in an email, never on a second exchange. Absent for auto-approvals, service-initiated and legacy requests, or when the human disconnected it before it was collected.\n"
                    },
                    "enrollment_note": {
                      "type": "string",
                      "description": "With enrollment: append SOMEBODY_ENROLLMENT=<value> to .env (create it if missing, keep its other lines, never commit it), load it next run, send it as `enrollment` on every /v1/start."
                    },
                    "verification": {
                      "$ref": "#/components/schemas/SyntheticVerification"
                    },
                    "next": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "202": {
            "description": "Still pending.",
            "headers": {
              "Retry-After": {
                "schema": {
                  "type": "integer"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "status": {
                      "type": "string",
                      "const": "pending"
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "retry_after": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Rejected by the human, or the PKCE verifier does not match (nothing is issued either way).",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "status": {
                      "type": "string",
                      "enum": [
                        "rejected"
                      ]
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "410": {
            "description": "Already delivered (replay), or expired before a decision.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "status": {
                      "type": "string",
                      "enum": [
                        "expired"
                      ]
                    },
                    "request_id": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "422": {
            "$ref": "#/components/responses/InvalidBody"
          }
        }
      }
    },
    "/v1/start": {
      "get": {
        "operationId": "startGet",
        "tags": [
          "agent-onramp"
        ],
        "summary": "The curl / CLI form of POST /v1/start — one call to an approval link, no copying.",
        "security": [],
        "parameters": [
          {
            "in": "query",
            "name": "service",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9][a-z0-9-]{1,62}$"
            }
          },
          {
            "in": "query",
            "name": "auth_type",
            "required": false,
            "schema": {
              "type": "string",
              "pattern": "^[a-z][a-z0-9_-]{1,31}$"
            }
          },
          {
            "in": "query",
            "name": "scopes",
            "required": false,
            "schema": {
              "type": "string",
              "description": "Comma-separated."
            }
          },
          {
            "in": "query",
            "name": "agent",
            "required": false,
            "schema": {
              "type": "string",
              "maxLength": 80
            }
          },
          {
            "in": "query",
            "name": "enrollment",
            "required": false,
            "schema": {
              "type": "string",
              "pattern": "^sb_e_[0-9a-f]{40}$"
            }
          }
        ],
        "responses": {
          "201": {
            "$ref": "#/components/responses/StartCreated"
          },
          "422": {
            "$ref": "#/components/responses/InvalidBody"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      },
      "post": {
        "operationId": "startPost",
        "tags": [
          "agent-onramp"
        ],
        "summary": "One call: creates the authorization request and returns a generated PKCE verifier (client_secret) plus the approval link to hand the human.",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "service"
                ],
                "properties": {
                  "service": {
                    "type": "string",
                    "pattern": "^[a-z0-9][a-z0-9-]{1,62}$"
                  },
                  "auth_type": {
                    "type": "string",
                    "pattern": "^[a-z][a-z0-9_-]{1,31}$"
                  },
                  "scopes": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  },
                  "agent": {
                    "type": "string",
                    "maxLength": 80
                  },
                  "enrollment": {
                    "type": "string",
                    "pattern": "^sb_e_[0-9a-f]{40}$"
                  }
                }
              },
              "examples": {
                "nohumans": {
                  "value": {
                    "service": "nohumans",
                    "auth_type": "named",
                    "agent": "henri"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "$ref": "#/components/responses/StartCreated"
          },
          "422": {
            "$ref": "#/components/responses/InvalidBody"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/pair/{code}": {
      "get": {
        "operationId": "pairClaim",
        "tags": [
          "agent-onramp"
        ],
        "summary": "An agent claims a human-created pairing link. Answers once, then 410.",
        "description": "Delivered by the human's account page (not an API a human calls by hand). An agent fetches this exactly once, with Accept: application/json (or any non-browser client), to collect its enrollment token; a second fetch is 410. A browser-style request (Accept text/html, or a browser user agent without an explicit JSON/Markdown accept) gets an explanatory HTML page, claims nothing and reveals nothing. Links expire 15 minutes after creation.\n",
        "security": [],
        "parameters": [
          {
            "in": "path",
            "name": "code",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-f]{24}$"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK (JSON for an agent). The HTML view never carries the credential.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "enrollment",
                    "connection",
                    "profile",
                    "services",
                    "keep",
                    "use"
                  ],
                  "properties": {
                    "enrollment": {
                      "type": "string",
                      "pattern": "^sb_e_[0-9a-f]{40}$"
                    },
                    "connection": {
                      "type": "string"
                    },
                    "profile": {
                      "type": "string"
                    },
                    "services": {
                      "oneOf": [
                        {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        },
                        {
                          "type": "string",
                          "const": "any"
                        }
                      ]
                    },
                    "keep": {
                      "type": "string",
                      "description": "v0.5: the .env instruction (SOMEBODY_ENROLLMENT)."
                    },
                    "use": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "No such pairing link.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "410": {
            "description": "Already claimed, or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/pair": {
      "post": {
        "operationId": "vouch",
        "tags": [
          "agent-onramp"
        ],
        "summary": "v0.5 (plan v3 §2c): a connected agent connects another agent with zero taps.",
        "description": "An agent holding a live enrollment mints a one-time pairing link (15 minutes) for a NEW connection on the same profile with the same bounds (services, access types, max grants; a profile-wide parent's per-service bindings too) — never wider. The new agent claims it with GET /pair/{code} and holds its own enrollment; either can be disconnected alone, and the human's \"Disconnect this and everything it connected\" stops both. The human is emailed once; the response and the email carry no credential and no account identifier. 10 per account per UTC day.\n",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "enrollment",
                  "label"
                ],
                "properties": {
                  "enrollment": {
                    "type": "string",
                    "pattern": "^sb_e_[0-9a-f]{40}$",
                    "description": "Your own connection credential."
                  },
                  "label": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 80,
                    "description": "A name for the new agent."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "A pairing link for the new agent.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "pair_url",
                    "expires_in",
                    "connection",
                    "note"
                  ],
                  "properties": {
                    "pair_url": {
                      "type": "string",
                      "format": "uri"
                    },
                    "expires_in": {
                      "type": "integer",
                      "const": 900
                    },
                    "connection": {
                      "type": "string"
                    },
                    "note": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "403": {
            "description": "Refused, with details {reason: enrollment_unknown_or_revoked | enrollment_exhausted | profile_archived | account_inactive, remedy}.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "$ref": "#/components/responses/InvalidBody"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/v1/me": {
      "get": {
        "operationId": "me",
        "tags": [
          "agent-onramp"
        ],
        "summary": "v0.6 (plan v3 §3 H3): who am I and what may I do — from the enrollment alone.",
        "description": "One secret recovers everything. Send the enrollment as `Authorization: Bearer sb_e_…` (or `?enrollment=` for curl). The answer (≈2 KiB) is a snapshot of state, never instructions: the connection, the profile it acts for, and per service it is bound to or authorized at — the access type and connection id used there, its active authorizations with live key counts, the account-wide rules with used/remaining/resets_at per window and blocks, and open reports filed against THIS connection's own keys (category, words, a note when the category is `other`; a service's note is that service's text). Lists stop at 10 with `more: true`. Nothing names the account, an email, a phone, a credential, or another connection. Unknown, disconnected, absent and malformed enrollments get one byte-identical 401 (no oracle). Consumes nothing.\n",
        "security": [],
        "parameters": [
          {
            "name": "enrollment",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "pattern": "^sb_e_[0-9a-f]{40}$"
            },
            "description": "For curl; prefer the Authorization header."
          }
        ],
        "responses": {
          "200": {
            "description": "The snapshot.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "connection",
                    "profile",
                    "services",
                    "standing",
                    "next",
                    "docs"
                  ],
                  "properties": {
                    "connection": {
                      "type": "object",
                      "properties": {
                        "label": {
                          "type": "string"
                        },
                        "created_at": {
                          "type": "string",
                          "format": "date-time"
                        },
                        "last_used_at": {
                          "type": "string",
                          "format": "date-time",
                          "nullable": true
                        },
                        "profile_wide": {
                          "type": "boolean"
                        },
                        "parent": {
                          "type": "boolean",
                          "description": "true when another agent connected this one (vouch)."
                        }
                      }
                    },
                    "profile": {
                      "type": "object",
                      "properties": {
                        "label": {
                          "type": "string"
                        },
                        "handle": {
                          "type": "string",
                          "nullable": true
                        },
                        "new_services": {
                          "type": "string",
                          "enum": [
                            "anonymous",
                            "named",
                            "ask"
                          ]
                        },
                        "archived": {
                          "type": "boolean"
                        }
                      }
                    },
                    "services": {
                      "type": "array",
                      "maxItems": 10,
                      "items": {
                        "type": "object",
                        "properties": {
                          "service": {
                            "type": "string"
                          },
                          "service_name": {
                            "type": "string"
                          },
                          "identity": {
                            "type": "string",
                            "enum": [
                              "anonymous",
                              "username",
                              "real_name"
                            ],
                            "nullable": true
                          },
                          "auth_type": {
                            "type": "string",
                            "nullable": true
                          },
                          "connection": {
                            "type": "string",
                            "pattern": "^con_[0-9a-f]{32}$",
                            "description": "identity.connection as this service sees this agent."
                          },
                          "grants": {
                            "type": "array",
                            "maxItems": 10,
                            "items": {
                              "type": "object",
                              "properties": {
                                "grant_id": {
                                  "type": "string",
                                  "format": "uuid"
                                },
                                "expires_at": {
                                  "type": "string",
                                  "format": "date-time",
                                  "nullable": true
                                },
                                "active_keys": {
                                  "type": "integer"
                                }
                              }
                            }
                          },
                          "rules": {
                            "type": "object",
                            "description": "What /v1/access/rules reports, for every limited bucket: limits.<bucket>.per_minute|per_day = {limit, used, remaining, resets_at}; blocks; active_keys; key_cap."
                          },
                          "reports_open": {
                            "type": "array",
                            "maxItems": 10,
                            "items": {
                              "type": "object",
                              "properties": {
                                "category": {
                                  "type": "string"
                                },
                                "words": {
                                  "type": "string"
                                },
                                "note": {
                                  "type": "string",
                                  "maxLength": 500
                                },
                                "created_at": {
                                  "type": "string",
                                  "format": "date-time"
                                }
                              }
                            }
                          },
                          "more": {
                            "type": "boolean"
                          }
                        }
                      }
                    },
                    "more": {
                      "type": "boolean"
                    },
                    "standing": {
                      "type": "string",
                      "enum": [
                        "ok",
                        "restricted",
                        "reported"
                      ]
                    },
                    "verification": {
                      "$ref": "#/components/schemas/SyntheticVerification"
                    },
                    "next": {
                      "type": "string",
                      "description": "One sentence: what to do now."
                    },
                    "docs": {
                      "type": "string",
                      "format": "uri"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "The one refusal: {error, reason: enrollment_unknown_or_revoked, remedy, docs}.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/keys": {
      "post": {
        "operationId": "mintKey",
        "tags": [
          "grant"
        ],
        "summary": "Mint a service-specific access key under a grant.",
        "description": "v0.7 (D11): minting beyond key_cap retires the oldest active keys of the key's CONNECTION at this service (across that connection's grants), or, for a grant without a connection, of the account × service as before (never above the ceiling). After a connection mint, keys beyond the service's account_key_ceiling in the account × service retire too, oldest first (listed in ceiling_retired). Same transaction, under the account × service lock. The secret is returned exactly once and never stored.\n",
        "security": [
          {
            "grantToken": []
          }
        ],
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "key",
                    "credential_id",
                    "service",
                    "expires_at",
                    "retired",
                    "active_keys",
                    "key_cap"
                  ],
                  "properties": {
                    "key": {
                      "type": "string",
                      "pattern": "^sb_k_[0-9a-f]{40}$"
                    },
                    "credential_id": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "service": {
                      "type": "string"
                    },
                    "expires_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "retired": {
                      "type": "array",
                      "items": {
                        "type": "string",
                        "format": "uuid"
                      },
                      "description": "Every key this mint retired, by the cap and by the ceiling."
                    },
                    "ceiling_retired": {
                      "type": "array",
                      "items": {
                        "type": "string",
                        "format": "uuid"
                      },
                      "description": "v0.7: the subset retired by the account ceiling."
                    },
                    "active_keys": {
                      "type": "integer"
                    },
                    "key_cap": {
                      "type": "integer"
                    },
                    "cap_scope": {
                      "type": "string",
                      "enum": [
                        "connection",
                        "account"
                      ],
                      "description": "v0.7 (D11): which scope the cap applied in — the key's connection (its grant has one) or, for a legacy grant, the account × service. active_keys and key_cap are counted in that scope."
                    },
                    "account_active_keys": {
                      "type": "integer",
                      "description": "v0.7: active keys of the account at this service, every connection."
                    },
                    "account_key_ceiling": {
                      "type": "integer",
                      "description": "v0.7: the hard stop on account_active_keys (default 30)."
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      },
      "get": {
        "operationId": "listKeys",
        "tags": [
          "grant"
        ],
        "summary": "List the keys of this grant's connection (v0.7; the account's keys for a grant without one).",
        "security": [
          {
            "grantToken": []
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "service",
                    "key_cap",
                    "active_keys",
                    "keys"
                  ],
                  "properties": {
                    "service": {
                      "type": "string"
                    },
                    "key_cap": {
                      "type": "integer"
                    },
                    "active_keys": {
                      "type": "integer"
                    },
                    "cap_scope": {
                      "type": "string",
                      "enum": [
                        "connection",
                        "account"
                      ],
                      "description": "v0.7 (D11): which scope the cap applied in — the key's connection (its grant has one) or, for a legacy grant, the account × service. active_keys and key_cap are counted in that scope."
                    },
                    "account_active_keys": {
                      "type": "integer",
                      "description": "v0.7: active keys of the account at this service, every connection."
                    },
                    "account_key_ceiling": {
                      "type": "integer",
                      "description": "v0.7: the hard stop on account_active_keys (default 30)."
                    },
                    "keys": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "credential_id": {
                            "type": "string",
                            "format": "uuid"
                          },
                          "status": {
                            "type": "string",
                            "enum": [
                              "active",
                              "retired",
                              "revoked",
                              "expired"
                            ]
                          },
                          "this_grant": {
                            "type": "boolean"
                          },
                          "issued_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "expires_at": {
                            "type": "string",
                            "format": "date-time",
                            "nullable": true
                          },
                          "ended_at": {
                            "type": "string",
                            "format": "date-time",
                            "nullable": true
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/v1/keys/{credential_id}/revoke": {
      "post": {
        "operationId": "revokeKey",
        "tags": [
          "grant"
        ],
        "summary": "Revoke one key belonging to this grant's account × service.",
        "security": [
          {
            "grantToken": []
          }
        ],
        "parameters": [
          {
            "in": "path",
            "name": "credential_id",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "credential_id": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "status": {
                      "type": "string",
                      "const": "revoked"
                    },
                    "active_keys": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "No such key under this account × service.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/v1/grant": {
      "get": {
        "operationId": "getGrant",
        "tags": [
          "grant"
        ],
        "summary": "Own grant.",
        "security": [
          {
            "grantToken": []
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "grant"
                  ],
                  "properties": {
                    "grant": {
                      "type": "object",
                      "properties": {
                        "grant_id": {
                          "type": "string",
                          "format": "uuid"
                        },
                        "service": {
                          "type": "string"
                        },
                        "scopes": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        },
                        "expires_at": {
                          "type": "string",
                          "format": "date-time",
                          "nullable": true
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/v1/grant/revoke": {
      "post": {
        "operationId": "revokeGrant",
        "tags": [
          "grant"
        ],
        "summary": "Revoke this grant and every key it minted.",
        "description": "Keys of another grant of the same account are unaffected.",
        "security": [
          {
            "grantToken": []
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "grant_id": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "status": {
                      "type": "string",
                      "const": "revoked"
                    },
                    "keys_revoked": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/v1/usernames/check": {
      "get": {
        "operationId": "usernameCheck",
        "tags": [
          "open"
        ],
        "summary": "Is this username available on Somebody? Never says who holds it.",
        "security": [],
        "parameters": [
          {
            "in": "query",
            "name": "name",
            "required": true,
            "schema": {
              "type": "string",
              "maxLength": 64
            }
          },
          {
            "in": "query",
            "name": "service",
            "required": false,
            "schema": {
              "type": "string",
              "maxLength": 64,
              "description": "Also check that service's own reserved list."
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "available": {
                      "type": "boolean"
                    },
                    "reason": {
                      "type": "string",
                      "enum": [
                        "format",
                        "taken",
                        "reserved"
                      ],
                      "nullable": true
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/profiles": {
      "get": {
        "operationId": "listProfiles",
        "tags": [
          "session"
        ],
        "summary": "This human's profiles.",
        "security": [
          {
            "sessionCookie": []
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Profile"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      },
      "post": {
        "operationId": "createProfile",
        "tags": [
          "session"
        ],
        "summary": "Create a profile (limit account.profile_limit, default 2 since 0036).",
        "security": [
          {
            "sessionCookie": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "label"
                ],
                "properties": {
                  "label": {
                    "type": "string",
                    "maxLength": 60,
                    "description": "The profile's private name."
                  },
                  "description": {
                    "type": "string",
                    "maxLength": 140
                  },
                  "handle": {
                    "type": "string",
                    "pattern": "^[a-z0-9][a-z0-9_-]{2,31}$",
                    "description": "Optional at creation; unique across Somebody; fixed once the profile has an active grant anywhere."
                  },
                  "display_name": {
                    "type": "string",
                    "maxLength": 64
                  },
                  "real_name": {
                    "type": "string",
                    "maxLength": 120,
                    "description": "Self-declared; encrypted at rest; real_name_verified stays false."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Profile"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "409": {
            "description": "Handle already taken.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "Malformed field, or profile_limit reached (code invalid_body, message names profile_limit).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/v1/profiles/{profile_id}": {
      "patch": {
        "operationId": "updateProfile",
        "tags": [
          "session"
        ],
        "summary": "Update or archive a profile.",
        "security": [
          {
            "sessionCookie": []
          }
        ],
        "parameters": [
          {
            "in": "path",
            "name": "profile_id",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "label": {
                    "type": "string",
                    "maxLength": 60
                  },
                  "description": {
                    "type": "string",
                    "maxLength": 140
                  },
                  "handle": {
                    "type": "string",
                    "pattern": "^[a-z0-9][a-z0-9_-]{2,31}$"
                  },
                  "display_name": {
                    "type": "string",
                    "maxLength": 64
                  },
                  "real_name": {
                    "type": "string",
                    "maxLength": 120
                  },
                  "clear_real_name": {
                    "type": "boolean"
                  },
                  "archive": {
                    "type": "boolean"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Profile"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "Handle already taken.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "$ref": "#/components/responses/InvalidBody"
          }
        }
      }
    },
    "/v1/enrollments": {
      "get": {
        "operationId": "listEnrollments",
        "tags": [
          "session"
        ],
        "summary": "This human's enrollment tokens (set-up-once connections).",
        "security": [
          {
            "sessionCookie": []
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "enrollments": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Enrollment"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      },
      "post": {
        "operationId": "createEnrollment",
        "tags": [
          "session"
        ],
        "summary": "Create an enrollment token that auto-approves future authorizations within its bounds. Up to 10 active; 50 grants each by default.",
        "security": [
          {
            "sessionCookie": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "label",
                  "profile_id"
                ],
                "properties": {
                  "label": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 80
                  },
                  "profile_id": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "services": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "Omit for any service."
                  },
                  "auth_types": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "Omit for any auth type."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created. token is shown once.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Enrollment"
                    },
                    {
                      "type": "object",
                      "required": [
                        "token"
                      ],
                      "properties": {
                        "token": {
                          "type": "string",
                          "pattern": "^sb_e_[0-9a-f]{40}$"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "422": {
            "$ref": "#/components/responses/InvalidBody"
          }
        }
      }
    },
    "/v1/enrollments/{enrollment_id}": {
      "delete": {
        "operationId": "revokeEnrollment",
        "tags": [
          "session"
        ],
        "summary": "Revoke an enrollment token. Stops future automatic approvals; grants it already made stay until revoked individually.",
        "security": [
          {
            "sessionCookie": []
          }
        ],
        "parameters": [
          {
            "in": "path",
            "name": "enrollment_id",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "enrollment_id": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "status": {
                      "type": "string",
                      "const": "revoked"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/waitlist": {
      "post": {
        "operationId": "waitlistSubmit",
        "tags": [
          "open"
        ],
        "summary": "Record an ask (not a sign-up). Creates no account, mints no key, notifies only the operator.",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "email"
                ],
                "properties": {
                  "email": {
                    "type": "string",
                    "format": "email",
                    "maxLength": 320
                  },
                  "note": {
                    "type": "string",
                    "maxLength": 500
                  },
                  "kind": {
                    "type": "string",
                    "enum": [
                      "human",
                      "service"
                    ],
                    "default": "human"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Recorded.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "const": true
                    }
                  }
                }
              }
            }
          },
          "422": {
            "$ref": "#/components/responses/InvalidBody"
          }
        }
      }
    },
    "/.well-known/oauth-authorization-server": {
      "get": {
        "operationId": "oauthMetadata",
        "tags": [
          "oauth"
        ],
        "summary": "RFC 8414 authorization server metadata for \"Sign in with Somebody\". Cacheable 5 minutes.",
        "security": [],
        "responses": {
          "200": {
            "description": "Authorization code + PKCE (S256) only; no refresh_token grant. `token_endpoint_auth_methods_supported` gains `none` and `client_id_metadata_document_supported: true` appears only when the deployment admits Client ID Metadata Document clients (OAUTH_CIMD, off by default).\n",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "issuer",
                    "authorization_endpoint",
                    "token_endpoint",
                    "response_types_supported",
                    "grant_types_supported",
                    "code_challenge_methods_supported",
                    "token_endpoint_auth_methods_supported",
                    "scopes_supported",
                    "service_documentation"
                  ],
                  "properties": {
                    "issuer": {
                      "type": "string",
                      "format": "uri"
                    },
                    "authorization_endpoint": {
                      "type": "string",
                      "format": "uri"
                    },
                    "token_endpoint": {
                      "type": "string",
                      "format": "uri"
                    },
                    "response_types_supported": {
                      "type": "array",
                      "items": {
                        "type": "string",
                        "const": "code"
                      }
                    },
                    "grant_types_supported": {
                      "type": "array",
                      "items": {
                        "type": "string",
                        "const": "authorization_code"
                      }
                    },
                    "code_challenge_methods_supported": {
                      "type": "array",
                      "items": {
                        "type": "string",
                        "const": "S256"
                      }
                    },
                    "token_endpoint_auth_methods_supported": {
                      "type": "array",
                      "items": {
                        "type": "string",
                        "enum": [
                          "client_secret_basic",
                          "client_secret_post",
                          "none"
                        ]
                      }
                    },
                    "scopes_supported": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      },
                      "description": "The union of the scopes offered by the access types of services registered as OAuth clients."
                    },
                    "service_documentation": {
                      "type": "string",
                      "format": "uri"
                    },
                    "client_id_metadata_document_supported": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/Unavailable"
          }
        }
      }
    },
    "/oauth/authorize": {
      "get": {
        "operationId": "oauthAuthorize",
        "tags": [
          "oauth"
        ],
        "summary": "Start \"Sign in with Somebody\" — a browser redirect to the one-screen consent page.",
        "description": "The client is a registered service (its `client_id` and exact redirect URIs are on its service row; its client secret is its service secret). Creates a service-initiated authorization request named for the SERVICE'S registered name (never a query parameter) and 302s to /approve/{request_id}. After the human presses Grant access, the browser is sent to `redirect_uri?code=sb_c_…&state=…` (only those two); after Reject, to `redirect_uri?error=access_denied&state=…`. An unknown client or a redirect_uri that is not an exact registered match is answered with a 400 HTML page and never redirected. Errors after that (response_type, PKCE, scope, auth_type) are redirected to the verified redirect_uri as `error`, `error_description`, `state` (RFC 6749 §4.1.2.1).\n",
        "security": [],
        "parameters": [
          {
            "name": "response_type",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "const": "code"
            }
          },
          {
            "name": "client_id",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The registered client id; or, only with OAUTH_CIMD on, an https URL of a Client ID Metadata Document (then `service` is required)."
          },
          {
            "name": "redirect_uri",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uri"
            },
            "description": "Exactly one of the registered redirect URIs (string equality; https only)."
          },
          {
            "name": "code_challenge",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[A-Za-z0-9_-]{43}$"
            },
            "description": "base64url(sha256(code_verifier)). PKCE is mandatory."
          },
          {
            "name": "code_challenge_method",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "const": "S256"
            }
          },
          {
            "name": "state",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "maxLength": 512
            },
            "description": "Returned unchanged with the code or the error."
          },
          {
            "name": "scope",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Space-separated; a subset of the access type's scopes (the default)."
          },
          {
            "name": "auth_type",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Default: the service's only access type; with several, `named` if offered; otherwise required."
          },
          {
            "name": "service",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "CIMD clients only: the Somebody service slug the grant is for."
          },
          {
            "name": "agent_label",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "maxLength": 80
            },
            "description": "v0.6.1: your name for the agent you will register under the human (e.g. web-agent-bb5edfc7). Trimmed; control characters and < > removed; empty means absent; more than 80 characters after cleaning is redirected as error=invalid_request, error_description=agent_label_too_long. Shown (escaped) on the consent screen and used to label the connection on the human's profile page; never echoed back to you."
          }
        ],
        "responses": {
          "302": {
            "description": "To /approve/{request_id}?oauth=1, or (an error after the redirect_uri is verified) to redirect_uri with error + state."
          },
          "400": {
            "description": "HTML page: unknown client, redirect_uri not an exact registered match, a repeated parameter, or a refused CIMD document. Never redirected."
          },
          "429": {
            "description": "HTML page: more than 60 authorize calls per hour from one address."
          },
          "503": {
            "description": "HTML page: the issuer is paused or cannot reach its store."
          }
        }
      }
    },
    "/oauth/token": {
      "post": {
        "operationId": "oauthToken",
        "tags": [
          "oauth"
        ],
        "summary": "Redeem a code (once) for a Somebody grant token. Client authentication is the service secret.",
        "description": "Client authentication: HTTP Basic `client_id:service_secret` (RFC 6749 §2.3.1) or `client_id` + `client_secret` in the body — exactly one. A CIMD public client (OAUTH_CIMD on) sends `client_id` only and is proven by PKCE alone. The code is `sb_c_…`, lives 10 minutes, is single use, and is bound to the client and the redirect_uri it was issued for; PKCE is checked against the challenge sent to /oauth/authorize. A code presented a second time issues nothing and revokes the grant its first use issued. No refresh tokens: the grant lasts its lifetime and keys rotate off it.\n",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/OAuthTokenRequest"
              }
            },
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/OAuthTokenRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The grant. `access_token` exists only in this response.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string",
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "access_token",
                    "token_type",
                    "expires_in",
                    "scope",
                    "identity",
                    "key_endpoint",
                    "note"
                  ],
                  "properties": {
                    "access_token": {
                      "type": "string",
                      "pattern": "^sb_g_[0-9a-f]{40}$",
                      "description": "A Somebody grant token: POST /v1/keys with it as Bearer to mint sb_k_ keys for this human at this service."
                    },
                    "token_type": {
                      "type": "string",
                      "const": "Bearer"
                    },
                    "expires_in": {
                      "type": "integer",
                      "description": "Seconds until the grant expires."
                    },
                    "expires_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "scope": {
                      "type": "string",
                      "description": "Space-separated."
                    },
                    "service": {
                      "type": "string"
                    },
                    "subject": {
                      "type": "string",
                      "pattern": "^sub_[0-9a-f]{32}$",
                      "nullable": true
                    },
                    "auth_type": {
                      "type": "string",
                      "nullable": true
                    },
                    "identity": {
                      "type": "object",
                      "properties": {
                        "subject": {
                          "type": "string",
                          "nullable": true
                        },
                        "auth_type": {
                          "type": "string",
                          "nullable": true
                        },
                        "connection": {
                          "type": "string",
                          "pattern": "^con_[0-9a-f]{32}$",
                          "description": "The connector, pairwise per service; the same value checks carry as identity.connection."
                        }
                      }
                    },
                    "connection": {
                      "type": "string",
                      "pattern": "^con_[0-9a-f]{32}$"
                    },
                    "verification": {
                      "$ref": "#/components/schemas/SyntheticVerification"
                    },
                    "key_endpoint": {
                      "type": "string",
                      "format": "uri"
                    },
                    "note": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "invalid_request | invalid_grant | unsupported_grant_type (RFC 6749 §5.2). invalid_grant issues nothing.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OAuthError"
                }
              }
            }
          },
          "401": {
            "description": "invalid_client: the client could not be authenticated (WWW-Authenticate: Basic when Basic was used).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OAuthError"
                }
              }
            }
          },
          "503": {
            "description": "temporarily_unavailable: paused, or the store is unreachable. Nothing is issued.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OAuthError"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "serviceSecret": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "sb_s_<40 hex>",
        "description": "A registered service's secret. Routes it for."
      },
      "grantToken": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "sb_g_<40 hex>",
        "description": "The minting token from a human-approved grant, held by the agent's client. Never sent to a service."
      },
      "sessionCookie": {
        "type": "apiKey",
        "in": "cookie",
        "name": "somebody_session",
        "description": "The human's signed-in session, set by /account or /approve/{id}. These routes are session-cookie-authenticated, not bearer-token routes; there is no way to call them with a service secret, grant token, or access key.\n"
      }
    },
    "responses": {
      "BadRequest": {
        "description": "Malformed request (e.g. an action not configured for this service).",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Unauthorized": {
        "description": "Missing, malformed, or wrong-shape credential for this route.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "NotFound": {
        "description": "Not found, or not visible to this caller.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Conflict": {
        "description": "Idempotency-Key reused with a different body, a duplicate, or a stale revision.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "PayloadTooLarge": {
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "InvalidBody": {
        "description": "The body failed validation.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "RateLimited": {
        "description": "Too many requests (e.g. more than 20 authorize creations per hour per IP).",
        "headers": {
          "Retry-After": {
            "schema": {
              "type": "integer"
            }
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Unavailable": {
        "description": "The issuer cannot reach its store. A service MUST deny protected writes on this response, not bypass enforcement (docs/contract/api.md \"Errors\"; plan §5).\n",
        "headers": {
          "Retry-After": {
            "schema": {
              "type": "integer"
            }
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "StartCreated": {
        "description": "Created.",
        "content": {
          "application/json": {
            "schema": {
              "type": "object",
              "required": [
                "request_id",
                "service",
                "expires_at",
                "approval_url",
                "client_secret",
                "poll",
                "human_instructions"
              ],
              "properties": {
                "request_id": {
                  "type": "string",
                  "format": "uuid"
                },
                "service": {
                  "type": "string"
                },
                "auth_type": {
                  "type": "string",
                  "nullable": true
                },
                "scopes": {
                  "type": "array",
                  "items": {
                    "type": "string"
                  }
                },
                "expires_at": {
                  "type": "string",
                  "format": "date-time"
                },
                "auto_approved": {
                  "type": "boolean",
                  "description": "When true, auth_type is the type granted (v0.5: a profile-wide connection may grant a narrower one than asked)."
                },
                "not_auto_approved_because": {
                  "type": "string",
                  "description": "A subset of RefusalReason.",
                  "enum": [
                    "service_not_enrolled",
                    "auth_type_not_enrolled",
                    "enrollment_unknown_or_revoked",
                    "enrollment_exhausted",
                    "type_needs_human_input",
                    "real_name_missing",
                    "verification_missing",
                    "country_not_allowed",
                    "profile_archived",
                    "service_has_no_auth_type",
                    "handle_missing",
                    "handle_reserved_at_service",
                    "service_offers_no_anonymous_type",
                    "profile_asks_first",
                    "connection_daily_rate"
                  ]
                },
                "remedy": {
                  "type": "string",
                  "description": "v0.6: with not_auto_approved_because, what to do (approval_url always still works)."
                },
                "docs": {
                  "type": "string",
                  "format": "uri",
                  "description": "v0.6: with not_auto_approved_because."
                },
                "approval_url": {
                  "type": "string",
                  "format": "uri"
                },
                "connect_url": {
                  "type": "string",
                  "format": "uri",
                  "description": "The link to hand the human for a lasting connection: /connect?service=…&agent=… — sign-in, account creation, mobile verification, profile naming and the connection in one run, ending in a pairing link the human pastes back; GET that once and keep its `enrollment`."
                },
                "client_secret": {
                  "type": "string",
                  "description": "The PKCE verifier Somebody generated and returns once; hold it and send it on /v1/authorize/exchange."
                },
                "poll": {
                  "type": "object",
                  "properties": {
                    "method": {
                      "type": "string",
                      "const": "POST"
                    },
                    "url": {
                      "type": "string",
                      "format": "uri"
                    },
                    "body": {
                      "type": "object"
                    },
                    "interval_seconds": {
                      "type": "integer"
                    },
                    "until": {
                      "type": "string"
                    }
                  }
                },
                "human_instructions": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "description": "v0.6 (H7): every refusal is {error, reason, remedy, docs, retry_after?}. `error` keeps its 0.1 shape; `reason` is one RefusalReason; `remedy` is what the caller or its human can actually do (never \"mint another key\"); `docs` is the guide to read. The GET /v1/me 401 omits error.request_id (it rides in X-Request-Id) so unknown and disconnected enrollments get byte-identical bodies.",
        "required": [
          "error",
          "reason",
          "remedy",
          "docs"
        ],
        "properties": {
          "error": {
            "type": "object",
            "required": [
              "code",
              "message"
            ],
            "properties": {
              "code": {
                "type": "string",
                "enum": [
                  "bad_request",
                  "unauthorized",
                  "forbidden",
                  "not_found",
                  "conflict",
                  "gone",
                  "rate_limited",
                  "payload_too_large",
                  "unsupported_media_type",
                  "invalid_body",
                  "unavailable",
                  "internal"
                ]
              },
              "message": {
                "type": "string"
              },
              "request_id": {
                "type": "string"
              },
              "retry_after": {
                "type": "integer"
              },
              "details": {
                "type": "object"
              }
            }
          },
          "reason": {
            "$ref": "#/components/schemas/RefusalReason"
          },
          "remedy": {
            "type": "string"
          },
          "docs": {
            "type": "string",
            "format": "uri"
          },
          "retry_after": {
            "type": "integer",
            "description": "Seconds; also the Retry-After header."
          }
        }
      },
      "RefusalReason": {
        "type": "string",
        "description": "v0.6 (H7): the ONE list of snake_case codes a refusal can carry, wherever it appears — an error body's `reason`, a check/rules deny's `reason` (DenyReason is a subset), a start/authorize `not_auto_approved_because`, a vouch refusal's `details.reason`, the exchange's rejected/expired bodies. Spellings are stable; a new code is added here first (the suite fails on any code it observes that is not listed). Meanings, one sentence each: docs/contract/api.md, v0.6 \"Refusal reasons\".\n",
        "enum": [
          "bad_request",
          "unauthorized",
          "forbidden",
          "not_found",
          "conflict",
          "gone",
          "rate_limited",
          "payload_too_large",
          "unsupported_media_type",
          "invalid_body",
          "unavailable",
          "internal",
          "unknown_credential",
          "credential_retired",
          "credential_revoked",
          "credential_expired",
          "account_suspended",
          "grant_revoked",
          "scope_missing",
          "missing_verification",
          "blocked",
          "throttled",
          "service_not_enrolled",
          "auth_type_not_enrolled",
          "enrollment_unknown_or_revoked",
          "enrollment_exhausted",
          "type_needs_human_input",
          "real_name_missing",
          "verification_missing",
          "country_not_allowed",
          "profile_archived",
          "service_has_no_auth_type",
          "handle_missing",
          "handle_reserved_at_service",
          "service_offers_no_anonymous_type",
          "profile_asks_first",
          "connection_daily_rate",
          "account_inactive",
          "grant_unknown",
          "grant_expired",
          "verifier_mismatch",
          "request_not_found",
          "already_delivered",
          "request_rejected",
          "request_expired",
          "reserved",
          "reserved_at_service",
          "profile_limit",
          "auth_type_limit",
          "username_unavailable",
          "connection_disconnected",
          "client_authentication_failed",
          "code_unknown",
          "code_already_used",
          "code_expired",
          "code_issued_to_another_client",
          "redirect_uri_mismatch",
          "code_verifier_mismatch",
          "public_client_disabled",
          "unsupported_grant_type",
          "not_approved"
        ]
      },
      "Enforcement": {
        "type": "object",
        "properties": {
          "limits": {
            "type": "object",
            "description": "bucket → {per_minute, per_day, code, until}. An override replaces the service default for that bucket wholesale and expires on its own at until.",
            "additionalProperties": {
              "type": "object",
              "properties": {
                "per_minute": {
                  "type": "integer"
                },
                "per_day": {
                  "type": "integer"
                },
                "code": {
                  "type": "string",
                  "maxLength": 64
                },
                "until": {
                  "type": "string",
                  "format": "date-time",
                  "description": "A literal ISO-8601 instant; Postgres's now/tomorrow are refused."
                }
              }
            }
          },
          "blocks": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "code"
              ],
              "properties": {
                "code": {
                  "type": "string",
                  "maxLength": 64
                },
                "until": {
                  "type": "string",
                  "format": "date-time",
                  "nullable": true
                },
                "reason": {
                  "type": "string",
                  "maxLength": 500
                }
              }
            }
          }
        }
      },
      "DenyReason": {
        "type": "string",
        "description": "A subset of RefusalReason (plus ok). v0.6: a deny also carries `docs`; a `blocked` deny's remedy names the newest open report's category and the appeal path.\nDeny reasons, distinguishable by the agent (docs/contract/api.md, plan §4):\n  unknown_credential   credential=unknown    not a key of this service\n  credential_retired   credential=retired     replaced by a newer key (N-key rule)\n  credential_revoked   credential=revoked     the human revoked it (or its grant)\n  credential_expired   credential=expired     lifetime reached; mint under the grant\n  account_suspended    credential=valid       the account is suspended at the issuer\n  grant_revoked        credential=valid       the authorization behind the key is gone\n  scope_missing        credential=valid       the grant lacks the action's scope (extra: scope)\n  missing_verification credential=valid       requirements unmet (extra: missing[])\n  blocked               credential=valid       a service block is active (extra: block, retry_after)\n  throttled              credential=valid       account-wide allowance exhausted (extra: bucket, window, retry_after)\n",
        "enum": [
          "ok",
          "unknown_credential",
          "credential_retired",
          "credential_revoked",
          "credential_expired",
          "account_suspended",
          "grant_revoked",
          "scope_missing",
          "missing_verification",
          "blocked",
          "throttled"
        ]
      },
      "Identity": {
        "type": "object",
        "description": "Present only on a grant made under an auth type (v0.4); absent entirely on a legacy grant. Only the fields that type releases are present; subject is always present when the block exists. subject is sub_ + 32 lowercase hex, minted at the profile's first grant for this service, stable for that profile at that service, never reused, and different at every other service — it is never an account id. connection (v0.5) names the AGENT, not the human: con_ + 32 lowercase hex, one per connection (enrollment) × service, stable across that agent's key rotation and re-authorizations, different for another connection of the same human and different at every other service. Released under every auth type for keys minted under a grant that has a connection; omitted on a grant with none (a service-initiated approval, or one made before v0.5 without an enrollment). Refuse one agent by it without touching the human's standing.\n",
        "required": [
          "subject"
        ],
        "properties": {
          "subject": {
            "type": "string",
            "pattern": "^sub_[0-9a-f]{32}$"
          },
          "handle": {
            "type": "string",
            "description": "Present under username or real_name identity types."
          },
          "display_name": {
            "type": "string",
            "description": "Present when the type releases the display name."
          },
          "real_name": {
            "type": "string",
            "description": "Present under the real_name identity type; self-declared."
          },
          "real_name_verified": {
            "type": "boolean",
            "const": false,
            "description": "Always false until an ID-document provider exists."
          },
          "country": {
            "type": "string",
            "description": "The verified mobile's country; absent when the type's country_rule is hidden."
          },
          "params": {
            "type": "object",
            "description": "The auth type's accepted/filled params, when it has any."
          },
          "connection": {
            "type": "string",
            "pattern": "^con_[0-9a-f]{32}$",
            "description": "v0.5: the agent's connection at this service (pairwise per connection × service). Absent on a grant with no connection."
          }
        }
      },
      "Rules": {
        "type": "object",
        "properties": {
          "scope": {
            "type": "string",
            "const": "account"
          },
          "shared_across_keys": {
            "type": "boolean",
            "const": true
          },
          "policy_version": {
            "type": "integer"
          },
          "active_keys": {
            "type": "integer",
            "description": "v0.7: counted in cap_scope (this key's connection, or the account for a legacy grant)."
          },
          "key_cap": {
            "type": "integer"
          },
          "cap_scope": {
            "type": "string",
            "enum": [
              "connection",
              "account"
            ]
          },
          "account_active_keys": {
            "type": "integer"
          },
          "account_key_ceiling": {
            "type": "integer"
          },
          "limits": {
            "type": "object",
            "additionalProperties": {
              "type": "object",
              "properties": {
                "per_minute": {
                  "type": "object",
                  "properties": {
                    "limit": {
                      "type": "integer"
                    },
                    "used": {
                      "type": "integer"
                    },
                    "remaining": {
                      "type": "integer"
                    },
                    "resets_at": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                },
                "per_day": {
                  "type": "object",
                  "properties": {
                    "limit": {
                      "type": "integer"
                    },
                    "used": {
                      "type": "integer"
                    },
                    "remaining": {
                      "type": "integer"
                    },
                    "resets_at": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                },
                "code": {
                  "type": "string"
                },
                "until": {
                  "type": "string",
                  "format": "date-time"
                }
              }
            }
          },
          "blocks": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "code": {
                  "type": "string"
                },
                "until": {
                  "type": "string",
                  "format": "date-time",
                  "nullable": true
                },
                "reason": {
                  "type": "string"
                }
              }
            }
          },
          "window_semantics": {
            "type": "string"
          }
        }
      },
      "PhonePolicy": {
        "type": "object",
        "description": "v0.8.1 — the issuer's phone step, in advance, on GET /v1/service. Composed from the issuer's configuration (no per-service setting); each auth type's country_rule narrows it further.\n",
        "required": [
          "line_types",
          "countries",
          "lookup_before_sms",
          "note"
        ],
        "properties": {
          "line_types": {
            "type": "array",
            "items": {
              "type": "string",
              "const": "mobile"
            }
          },
          "countries": {
            "oneOf": [
              {
                "type": "string",
                "const": "any"
              },
              {
                "type": "array",
                "items": {
                  "type": "string",
                  "pattern": "^[A-Z]{2}$"
                }
              }
            ]
          },
          "lookup_before_sms": {
            "type": "boolean",
            "const": true,
            "description": "The line type is looked up first; a number that cannot qualify costs no SMS."
          },
          "note": {
            "type": "string"
          }
        },
        "example": {
          "line_types": [
            "mobile"
          ],
          "countries": "any",
          "lookup_before_sms": true,
          "note": "Landline, VoIP and unclassified numbers are refused before any SMS; a service is told only that the mobile is verified, and the country only under a type whose country_rule is disclosed or required."
        }
      },
      "SyntheticVerification": {
        "type": "object",
        "description": "v0.8 (D12): present only for a platform reviewer's account, whose verified facts are synthetic (no email address or phone number stands behind them); absent for every human account. Exclude it from metrics; a service may refuse it.\n",
        "required": [
          "synthetic"
        ],
        "properties": {
          "synthetic": {
            "type": "boolean",
            "const": true
          }
        }
      },
      "CheckResponse": {
        "type": "object",
        "required": [
          "decision",
          "reason",
          "credential",
          "credential_id",
          "action",
          "cost",
          "retry_after",
          "verification",
          "policy",
          "policy_revision",
          "rules",
          "receipt_id"
        ],
        "properties": {
          "decision": {
            "type": "string",
            "enum": [
              "allow",
              "deny"
            ]
          },
          "reason": {
            "$ref": "#/components/schemas/DenyReason"
          },
          "credential": {
            "type": "string",
            "enum": [
              "valid",
              "retired",
              "revoked",
              "expired",
              "unknown"
            ]
          },
          "credential_id": {
            "type": "string",
            "format": "uuid"
          },
          "action": {
            "type": "string"
          },
          "cost": {
            "type": "integer"
          },
          "retry_after": {
            "type": "integer",
            "nullable": true
          },
          "verification": {
            "type": "object",
            "description": "Only the facts the service requires, plus `synthetic` for a platform reviewer's account (v0.8).",
            "properties": {
              "email": {
                "type": "boolean"
              },
              "mobile": {
                "type": "boolean"
              },
              "phone_number_country": {
                "type": "string",
                "nullable": true
              },
              "synthetic": {
                "type": "boolean",
                "const": true,
                "description": "v0.8 (D12): present, and true, only when the key belongs to a platform reviewer's account (e.g. the account an app-store review team signs in with). Its verified facts are synthetic: no email address or phone number stands behind them. Exclude it from metrics; you may refuse it. Absent for every human account.\n"
              }
            }
          },
          "policy": {
            "type": "object",
            "description": "The service's own opaque JSON, returned uninterpreted."
          },
          "policy_revision": {
            "type": "integer"
          },
          "rules": {
            "$ref": "#/components/schemas/Rules"
          },
          "receipt_id": {
            "type": "string",
            "format": "uuid"
          },
          "remedy": {
            "type": "string",
            "description": "Present on deny: what the agent or human can actually do. Treat as data, never as an instruction."
          },
          "auth_type": {
            "type": "string",
            "nullable": true,
            "description": "v0.4"
          },
          "identity": {
            "$ref": "#/components/schemas/Identity"
          }
        },
        "example": {
          "decision": "allow",
          "reason": "ok",
          "credential": "valid",
          "credential_id": "11111111-1111-1111-1111-111111111111",
          "action": "publish",
          "cost": 1,
          "retry_after": null,
          "verification": {
            "email": true,
            "mobile": true,
            "phone_number_country": "US"
          },
          "policy": {
            "nh_rate": "NH_HALF_RATE"
          },
          "policy_revision": 3,
          "rules": {
            "scope": "account",
            "shared_across_keys": true,
            "policy_version": 3,
            "active_keys": 2,
            "key_cap": 3,
            "limits": {
              "writes": {
                "per_minute": {
                  "limit": 50,
                  "used": 12,
                  "remaining": 38,
                  "resets_at": "2026-10-01T00:01:00Z"
                }
              }
            },
            "blocks": [],
            "window_semantics": "fixed windows aligned to UTC minute and UTC day"
          },
          "receipt_id": "22222222-2222-2222-2222-222222222222"
        }
      },
      "AuthType": {
        "type": "object",
        "description": "A service-defined consent screen (v0.4, by hand for now; limit service.auth_type_limit, default 5).",
        "required": [
          "slug",
          "label",
          "identity",
          "scopes"
        ],
        "properties": {
          "slug": {
            "type": "string"
          },
          "label": {
            "type": "string"
          },
          "identity": {
            "type": "string",
            "enum": [
              "anonymous",
              "username",
              "real_name"
            ]
          },
          "release_display_name": {
            "type": "boolean"
          },
          "country_rule": {
            "type": "object",
            "description": "Which mobiles this type accepts, within the issuer's phone_policy. hidden: any country, never released; disclosed: any country, released as identity.country; required: only `countries` (shown to the human before they verify), released as identity.country.\n",
            "properties": {
              "mode": {
                "type": "string",
                "enum": [
                  "hidden",
                  "disclosed",
                  "required"
                ]
              },
              "countries": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              }
            }
          },
          "auth_level": {
            "type": "string",
            "enum": [
              "email",
              "phone"
            ],
            "description": "id_document is refused until a provider exists."
          },
          "scopes": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "params": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "name": {
                  "type": "string"
                },
                "label": {
                  "type": "string"
                },
                "kind": {
                  "type": "string",
                  "enum": [
                    "accept",
                    "text"
                  ]
                },
                "pattern": {
                  "type": "string"
                }
              }
            }
          },
          "key_cap": {
            "type": "integer",
            "nullable": true
          },
          "key_lifetime_seconds": {
            "type": "integer",
            "nullable": true
          },
          "grant_lifetime_seconds": {
            "type": "integer",
            "nullable": true
          }
        }
      },
      "Profile": {
        "type": "object",
        "required": [
          "profile_id",
          "label",
          "status",
          "created_at"
        ],
        "properties": {
          "profile_id": {
            "type": "string",
            "format": "uuid"
          },
          "label": {
            "type": "string",
            "description": "Private name (D9); not released to a service."
          },
          "description": {
            "type": "string",
            "nullable": true
          },
          "handle": {
            "type": "string",
            "nullable": true,
            "description": "The username; optional at creation, fixed once the profile has an active grant anywhere."
          },
          "display_name": {
            "type": "string",
            "nullable": true
          },
          "real_name_set": {
            "type": "boolean"
          },
          "real_name_verified": {
            "type": "boolean",
            "const": false
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "archived"
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "new_services": {
            "type": "string",
            "enum": [
              "anonymous",
              "named",
              "ask"
            ],
            "description": "v0.5: what a profile-wide connection may do at a service it has not used. Default anonymous. Set on the profile page."
          }
        }
      },
      "Enrollment": {
        "type": "object",
        "required": [
          "enrollment_id",
          "label",
          "profile_id",
          "max_grants",
          "grants_made",
          "status",
          "created_at"
        ],
        "properties": {
          "enrollment_id": {
            "type": "string",
            "format": "uuid"
          },
          "label": {
            "type": "string"
          },
          "profile": {
            "type": "string"
          },
          "profile_id": {
            "type": "string",
            "format": "uuid"
          },
          "services": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "nullable": true
          },
          "auth_types": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "nullable": true
          },
          "max_grants": {
            "type": "integer",
            "nullable": true,
            "description": "v0.6: null for a profile-wide connection, which has no lifetime cap (a daily rate of 20 auto-approvals instead)."
          },
          "grants_made": {
            "type": "integer"
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "revoked"
            ]
          },
          "last_used_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "profile_wide": {
            "type": "boolean",
            "description": "v0.5: made by a hand approval; reaches any participating service under the profile's new_services setting."
          },
          "bound": {
            "type": "array",
            "description": "v0.5: per service, the access type a profile-wide connection is bound to (its ceiling there).",
            "items": {
              "type": "object",
              "properties": {
                "service": {
                  "type": "string"
                },
                "service_name": {
                  "type": "string"
                },
                "auth_type": {
                  "type": "string"
                },
                "identity": {
                  "type": "string"
                }
              }
            }
          },
          "connected": {
            "type": "integer",
            "description": "v0.5: active connections descended from this one by vouch."
          },
          "kind": {
            "type": "string",
            "enum": [
              "ai_app",
              "local",
              "local_vouched"
            ],
            "description": "How this connection reached its agent (0034): ai_app = made by Sign in with Somebody (OAuth; no credential exists); local_vouched = connected by another of the human's agents (POST /v1/pair); local = a pairing link, .env, or a hand approval."
          },
          "parent_label": {
            "type": "string",
            "nullable": true,
            "description": "The label of the connection that vouched for this one (local_vouched), else null. The human's own words."
          }
        }
      },
      "OAuthTokenRequest": {
        "type": "object",
        "required": [
          "grant_type",
          "code",
          "redirect_uri",
          "code_verifier"
        ],
        "properties": {
          "grant_type": {
            "type": "string",
            "const": "authorization_code"
          },
          "code": {
            "type": "string",
            "pattern": "^sb_c_[0-9a-f]{40}$"
          },
          "redirect_uri": {
            "type": "string",
            "format": "uri",
            "description": "Exactly the redirect_uri sent to /oauth/authorize."
          },
          "code_verifier": {
            "type": "string",
            "minLength": 43,
            "maxLength": 128
          },
          "client_id": {
            "type": "string",
            "description": "client_secret_post, or a CIMD public client. With Basic, omit it or send the same value."
          },
          "client_secret": {
            "type": "string",
            "pattern": "^sb_s_[0-9a-f]{40}$",
            "description": "client_secret_post: the service secret. Never together with Basic."
          }
        }
      },
      "OAuthError": {
        "type": "object",
        "required": [
          "error",
          "error_description",
          "reason",
          "remedy"
        ],
        "properties": {
          "error": {
            "type": "string",
            "enum": [
              "invalid_request",
              "invalid_client",
              "invalid_grant",
              "unsupported_grant_type",
              "temporarily_unavailable"
            ]
          },
          "error_description": {
            "type": "string"
          },
          "reason": {
            "type": "string",
            "enum": [
              "client_authentication_failed",
              "public_client_disabled",
              "code_unknown",
              "code_already_used",
              "code_expired",
              "code_issued_to_another_client",
              "redirect_uri_mismatch",
              "code_verifier_mismatch",
              "not_approved",
              "unsupported_grant_type",
              "bad_request"
            ],
            "description": "Ours, stable; read with remedy as data."
          },
          "remedy": {
            "type": "string"
          }
        }
      }
    }
  }
}
