{
  "openapi": "3.1.1",
  "info": {
    "title": "AfricaOne Developer API",
    "version": "1.1.0",
    "description": "Sandbox contract: public metadata, reviewed city discovery, OAuth 2.0 client credentials and authorization code with PKCE (user consent on africaone.io), refresh-token rotation, RFC 7009 revocation and the consented profile. Commerce, game sessions, webhooks management and production credentials are not in this contract and answer FEATURE_DISABLED. The platform is switched off in production unless DEVELOPER_PLATFORM_ENABLED is set."
  },
  "servers": [
    {
      "url": "http://127.0.0.1:4331/developer/v1",
      "description": "Local contract mock generated from this document (pnpm --filter @ao/developer-gateway mock). No credentials, no player data."
    },
    {
      "url": "https://api.africaone.io/developer/v1",
      "description": "Sandbox audience. Answers FEATURE_DISABLED while the platform is switched off."
    }
  ],
  "paths": {
    "/capabilities": {
      "get": {
        "operationId": "getCapabilities",
        "summary": "Inspect availability flags",
        "tags": ["Discovery"],
        "x-release-status": "sandbox",
        "x-audit-classification": "public-metadata-read",
        "x-quota-status": "pending-integration",
        "security": [],
        "responses": {
          "200": {
            "description": "Sanitized metadata.",
            "headers": {
              "X-Request-ID": {
                "description": "Server-generated opaque request correlation ID.",
                "schema": {
                  "type": "string"
                }
              },
              "X-AfricaOne-API-Version": {
                "schema": {
                  "const": "1.0"
                }
              },
              "Cache-Control": {
                "schema": {
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Capabilities"
                }
              }
            }
          },
          "404": {
            "description": "Fail-closed RFC 9457 Problem Details. No stack traces or credentials.",
            "headers": {
              "X-Request-ID": {
                "description": "Server-generated opaque request correlation ID.",
                "schema": {
                  "type": "string"
                }
              },
              "X-AfricaOne-API-Version": {
                "schema": {
                  "const": "1.0"
                }
              },
              "Cache-Control": {
                "schema": {
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "405": {
            "description": "Fail-closed RFC 9457 Problem Details. No stack traces or credentials.",
            "headers": {
              "X-Request-ID": {
                "description": "Server-generated opaque request correlation ID.",
                "schema": {
                  "type": "string"
                }
              },
              "X-AfricaOne-API-Version": {
                "schema": {
                  "const": "1.0"
                }
              },
              "Cache-Control": {
                "schema": {
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "503": {
            "description": "Fail-closed RFC 9457 Problem Details. No stack traces or credentials.",
            "headers": {
              "X-Request-ID": {
                "description": "Server-generated opaque request correlation ID.",
                "schema": {
                  "type": "string"
                }
              },
              "X-AfricaOne-API-Version": {
                "schema": {
                  "const": "1.0"
                }
              },
              "Cache-Control": {
                "schema": {
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        },
        "description": "Which capabilities this deployment serves, as booleans. A capability that is not integrated or is switched off is false; commerce and regulated assets are always false in this contract."
      }
    },
    "/regions": {
      "get": {
        "operationId": "listRegions",
        "summary": "Countries with listed cities, from the reviewed registry",
        "tags": ["Discovery"],
        "x-release-status": "sandbox",
        "x-audit-classification": "public-metadata-read",
        "x-quota-status": "pending-integration",
        "security": [],
        "responses": {
          "200": {
            "description": "Sanitized metadata.",
            "headers": {
              "X-Request-ID": {
                "description": "Server-generated opaque request correlation ID.",
                "schema": {
                  "type": "string"
                }
              },
              "X-AfricaOne-API-Version": {
                "schema": {
                  "const": "1.0"
                }
              },
              "Cache-Control": {
                "schema": {
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Regions"
                }
              }
            }
          },
          "404": {
            "description": "Fail-closed RFC 9457 Problem Details. No stack traces or credentials.",
            "headers": {
              "X-Request-ID": {
                "description": "Server-generated opaque request correlation ID.",
                "schema": {
                  "type": "string"
                }
              },
              "X-AfricaOne-API-Version": {
                "schema": {
                  "const": "1.0"
                }
              },
              "Cache-Control": {
                "schema": {
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "405": {
            "description": "Fail-closed RFC 9457 Problem Details. No stack traces or credentials.",
            "headers": {
              "X-Request-ID": {
                "description": "Server-generated opaque request correlation ID.",
                "schema": {
                  "type": "string"
                }
              },
              "X-AfricaOne-API-Version": {
                "schema": {
                  "const": "1.0"
                }
              },
              "Cache-Control": {
                "schema": {
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "503": {
            "description": "Fail-closed RFC 9457 Problem Details. No stack traces or credentials.",
            "headers": {
              "X-Request-ID": {
                "description": "Server-generated opaque request correlation ID.",
                "schema": {
                  "type": "string"
                }
              },
              "X-AfricaOne-API-Version": {
                "schema": {
                  "const": "1.0"
                }
              },
              "Cache-Control": {
                "schema": {
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        },
        "description": "Each entry is a country with at least one listed city: ISO 3166-1 alpha-2 code, name, the continental region as the registry names it, and the city ids with their availability. Public sanitised metadata; no live world state, no player data. Empty while no city is listed."
      }
    },
    "/status": {
      "get": {
        "operationId": "getStatus",
        "summary": "Developer API readiness and the public platform status",
        "tags": ["Discovery"],
        "x-release-status": "sandbox",
        "x-audit-classification": "public-metadata-read",
        "x-quota-status": "pending-integration",
        "security": [],
        "responses": {
          "200": {
            "description": "Sanitized metadata.",
            "headers": {
              "X-Request-ID": {
                "description": "Server-generated opaque request correlation ID.",
                "schema": {
                  "type": "string"
                }
              },
              "X-AfricaOne-API-Version": {
                "schema": {
                  "const": "1.0"
                }
              },
              "Cache-Control": {
                "schema": {
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Status"
                }
              }
            }
          },
          "404": {
            "description": "Fail-closed RFC 9457 Problem Details. No stack traces or credentials.",
            "headers": {
              "X-Request-ID": {
                "description": "Server-generated opaque request correlation ID.",
                "schema": {
                  "type": "string"
                }
              },
              "X-AfricaOne-API-Version": {
                "schema": {
                  "const": "1.0"
                }
              },
              "Cache-Control": {
                "schema": {
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "405": {
            "description": "Fail-closed RFC 9457 Problem Details. No stack traces or credentials.",
            "headers": {
              "X-Request-ID": {
                "description": "Server-generated opaque request correlation ID.",
                "schema": {
                  "type": "string"
                }
              },
              "X-AfricaOne-API-Version": {
                "schema": {
                  "const": "1.0"
                }
              },
              "Cache-Control": {
                "schema": {
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "503": {
            "description": "Fail-closed RFC 9457 Problem Details. No stack traces or credentials.",
            "headers": {
              "X-Request-ID": {
                "description": "Server-generated opaque request correlation ID.",
                "schema": {
                  "type": "string"
                }
              },
              "X-AfricaOne-API-Version": {
                "schema": {
                  "const": "1.0"
                }
              },
              "Cache-Control": {
                "schema": {
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        },
        "description": "`status` is the developer API’s own readiness (`sandbox`: sandbox audience only; `limited`: otherwise). `platform` is the public status projection behind status.africaone.io, sanitised to the overall state and component codes, or null when it is not integrated or temporarily unavailable (an outage of the status source never fails this endpoint)."
      }
    },
    "/cities": {
      "get": {
        "operationId": "listCities",
        "summary": "Read reviewed city metadata",
        "tags": ["Cities"],
        "x-release-status": "sandbox",
        "x-audit-classification": "app-scoped-metadata-read",
        "x-quota-status": "pending-integration",
        "security": [
          {
            "developerBearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "Sanitized metadata.",
            "headers": {
              "X-Request-ID": {
                "description": "Server-generated opaque request correlation ID.",
                "schema": {
                  "type": "string"
                }
              },
              "X-AfricaOne-API-Version": {
                "schema": {
                  "const": "1.0"
                }
              },
              "Cache-Control": {
                "schema": {
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Cities"
                }
              }
            }
          },
          "400": {
            "description": "Fail-closed RFC 9457 Problem Details. No stack traces or credentials.",
            "headers": {
              "X-Request-ID": {
                "description": "Server-generated opaque request correlation ID.",
                "schema": {
                  "type": "string"
                }
              },
              "X-AfricaOne-API-Version": {
                "schema": {
                  "const": "1.0"
                }
              },
              "Cache-Control": {
                "schema": {
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "401": {
            "description": "Fail-closed RFC 9457 Problem Details. No stack traces or credentials.",
            "headers": {
              "X-Request-ID": {
                "description": "Server-generated opaque request correlation ID.",
                "schema": {
                  "type": "string"
                }
              },
              "X-AfricaOne-API-Version": {
                "schema": {
                  "const": "1.0"
                }
              },
              "Cache-Control": {
                "schema": {
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "Fail-closed RFC 9457 Problem Details. No stack traces or credentials.",
            "headers": {
              "X-Request-ID": {
                "description": "Server-generated opaque request correlation ID.",
                "schema": {
                  "type": "string"
                }
              },
              "X-AfricaOne-API-Version": {
                "schema": {
                  "const": "1.0"
                }
              },
              "Cache-Control": {
                "schema": {
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "Fail-closed RFC 9457 Problem Details. No stack traces or credentials.",
            "headers": {
              "X-Request-ID": {
                "description": "Server-generated opaque request correlation ID.",
                "schema": {
                  "type": "string"
                }
              },
              "X-AfricaOne-API-Version": {
                "schema": {
                  "const": "1.0"
                }
              },
              "Cache-Control": {
                "schema": {
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "405": {
            "description": "Fail-closed RFC 9457 Problem Details. No stack traces or credentials.",
            "headers": {
              "X-Request-ID": {
                "description": "Server-generated opaque request correlation ID.",
                "schema": {
                  "type": "string"
                }
              },
              "X-AfricaOne-API-Version": {
                "schema": {
                  "const": "1.0"
                }
              },
              "Cache-Control": {
                "schema": {
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "503": {
            "description": "Fail-closed RFC 9457 Problem Details. No stack traces or credentials.",
            "headers": {
              "X-Request-ID": {
                "description": "Server-generated opaque request correlation ID.",
                "schema": {
                  "type": "string"
                }
              },
              "X-AfricaOne-API-Version": {
                "schema": {
                  "const": "1.0"
                }
              },
              "Cache-Control": {
                "schema": {
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        },
        "description": "Reviewed city registry projection (the same data GET /api/v1/cities serves publicly). Requires an app or user token with city.public.read. No signed pagination yet: a result larger than limit returns 503 instead of dropping records. No live world state or player data.",
        "x-required-scopes": ["city.public.read"],
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 25
            },
            "description": "Unknown, duplicate and cursor query parameters are rejected."
          }
        ]
      }
    },
    "/.well-known/oauth-authorization-server": {
      "get": {
        "operationId": "getAuthorizationServerMetadata",
        "summary": "OAuth 2.0 authorization server metadata (RFC 8414)",
        "description": "Issuer, authorization endpoint (the consent screen on the player client), token and revocation endpoints, supported grants (client_credentials, authorization_code, refresh_token), PKCE S256 only, and the scope catalogue.",
        "tags": ["OAuth"],
        "x-release-status": "sandbox",
        "x-audit-classification": "public-metadata-read",
        "x-quota-status": "pending-integration",
        "security": [],
        "responses": {
          "200": {
            "description": "Authorization server metadata.",
            "headers": {
              "X-Request-ID": {
                "description": "Server-generated opaque request correlation ID.",
                "schema": {
                  "type": "string"
                }
              },
              "X-AfricaOne-API-Version": {
                "schema": {
                  "const": "1.0"
                }
              },
              "Cache-Control": {
                "schema": {
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthorizationServerMetadata"
                }
              }
            }
          },
          "404": {
            "description": "Fail-closed RFC 9457 Problem Details. No stack traces or credentials.",
            "headers": {
              "X-Request-ID": {
                "description": "Server-generated opaque request correlation ID.",
                "schema": {
                  "type": "string"
                }
              },
              "X-AfricaOne-API-Version": {
                "schema": {
                  "const": "1.0"
                }
              },
              "Cache-Control": {
                "schema": {
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "405": {
            "description": "Fail-closed RFC 9457 Problem Details. No stack traces or credentials.",
            "headers": {
              "X-Request-ID": {
                "description": "Server-generated opaque request correlation ID.",
                "schema": {
                  "type": "string"
                }
              },
              "X-AfricaOne-API-Version": {
                "schema": {
                  "const": "1.0"
                }
              },
              "Cache-Control": {
                "schema": {
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "503": {
            "description": "User authorization is not integrated, or the platform is switched off.",
            "headers": {
              "X-Request-ID": {
                "description": "Server-generated opaque request correlation ID.",
                "schema": {
                  "type": "string"
                }
              },
              "X-AfricaOne-API-Version": {
                "schema": {
                  "const": "1.0"
                }
              },
              "Cache-Control": {
                "schema": {
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/oauth/token": {
      "post": {
        "operationId": "issueToken",
        "summary": "Issue an access token (RFC 6749)",
        "description": "Client authentication by HTTP Basic (client_secret_basic) or form fields (client_secret_post), exactly one. grant_type=client_credentials: a 300 s app token (sub = client id; app scopes only, never a user scope). grant_type=authorization_code: redeems a single-use code from the consent screen with PKCE S256 (code_verifier) and the exact redirect_uri; returns a 300 s user token (sub = per-app pairwise subject, never a player id) and a refresh token. grant_type=refresh_token: rotates the refresh token; presenting a used one revokes the whole family and the consent. No Idempotency-Key: the single-use code or refresh token is the replay control (CCR-089 §6).",
        "tags": ["OAuth"],
        "x-release-status": "sandbox",
        "x-audit-classification": "credential-exchange",
        "x-quota-status": "pending-integration",
        "security": [
          {
            "clientSecretBasic": []
          },
          {}
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/TokenRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "A token. Cache-Control: no-store.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TokenResponse"
                }
              }
            }
          },
          "400": {
            "description": "invalid_request, invalid_grant (unknown, used, expired or mismatched code or refresh token; PKCE failed), invalid_scope or unsupported_grant_type.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OAuthError"
                }
              }
            }
          },
          "401": {
            "description": "invalid_client: client authentication failed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OAuthError"
                }
              }
            }
          },
          "405": {
            "description": "Fail-closed RFC 9457 Problem Details. No stack traces or credentials.",
            "headers": {
              "X-Request-ID": {
                "description": "Server-generated opaque request correlation ID.",
                "schema": {
                  "type": "string"
                }
              },
              "X-AfricaOne-API-Version": {
                "schema": {
                  "const": "1.0"
                }
              },
              "Cache-Control": {
                "schema": {
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "503": {
            "description": "Token issuance is not configured, or the platform is switched off.",
            "headers": {
              "X-Request-ID": {
                "description": "Server-generated opaque request correlation ID.",
                "schema": {
                  "type": "string"
                }
              },
              "X-AfricaOne-API-Version": {
                "schema": {
                  "const": "1.0"
                }
              },
              "Cache-Control": {
                "schema": {
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/oauth/revoke": {
      "post": {
        "operationId": "revokeToken",
        "summary": "Revoke a refresh token or a user access token (RFC 7009)",
        "description": "Client-authenticated. Ends the consent the token was issued under, so every access token issued under it stops on its next use and every refresh token in the family dies. Always 200 for an unknown or foreign token.",
        "tags": ["OAuth"],
        "x-release-status": "sandbox",
        "x-audit-classification": "credential-revocation",
        "x-quota-status": "pending-integration",
        "security": [
          {
            "clientSecretBasic": []
          },
          {}
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/RevokeRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Revoked, or nothing to revoke. Empty body."
          },
          "400": {
            "description": "invalid_request: token missing.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OAuthError"
                }
              }
            }
          },
          "401": {
            "description": "invalid_client: client authentication failed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OAuthError"
                }
              }
            }
          },
          "405": {
            "description": "Fail-closed RFC 9457 Problem Details. No stack traces or credentials.",
            "headers": {
              "X-Request-ID": {
                "description": "Server-generated opaque request correlation ID.",
                "schema": {
                  "type": "string"
                }
              },
              "X-AfricaOne-API-Version": {
                "schema": {
                  "const": "1.0"
                }
              },
              "Cache-Control": {
                "schema": {
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "503": {
            "description": "Revocation is not integrated, or the platform is switched off.",
            "headers": {
              "X-Request-ID": {
                "description": "Server-generated opaque request correlation ID.",
                "schema": {
                  "type": "string"
                }
              },
              "X-AfricaOne-API-Version": {
                "schema": {
                  "const": "1.0"
                }
              },
              "Cache-Control": {
                "schema": {
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    },
    "/me": {
      "get": {
        "operationId": "getMe",
        "summary": "The consented profile of the authorising player",
        "description": "User tokens only (authorization_code or refresh_token grant): sub is the pairwise subject this app sees for the player (stable per app, unlinkable across apps, never a player id); display_name is present with profile.basic.read and null otherwise. An app token answers 403 USER_TOKEN_REQUIRED. A revoked consent answers 403 CONSENT_REVOKED on the next call.",
        "tags": ["Identity"],
        "x-release-status": "sandbox",
        "x-audit-classification": "consented-profile-read",
        "x-quota-status": "pending-integration",
        "x-required-scopes": ["profile.basic.read"],
        "security": [
          {
            "developerBearer": ["profile.basic.read"]
          }
        ],
        "responses": {
          "200": {
            "description": "The consented profile.",
            "headers": {
              "X-Request-ID": {
                "description": "Server-generated opaque request correlation ID.",
                "schema": {
                  "type": "string"
                }
              },
              "X-AfricaOne-API-Version": {
                "schema": {
                  "const": "1.0"
                }
              },
              "Cache-Control": {
                "schema": {
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Me"
                }
              }
            }
          },
          "401": {
            "description": "Fail-closed RFC 9457 Problem Details. No stack traces or credentials.",
            "headers": {
              "X-Request-ID": {
                "description": "Server-generated opaque request correlation ID.",
                "schema": {
                  "type": "string"
                }
              },
              "X-AfricaOne-API-Version": {
                "schema": {
                  "const": "1.0"
                }
              },
              "Cache-Control": {
                "schema": {
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "403": {
            "description": "ACCESS_DENIED, CONSENT_REVOKED, USER_TOKEN_REQUIRED or INSUFFICIENT_SCOPE.",
            "headers": {
              "X-Request-ID": {
                "description": "Server-generated opaque request correlation ID.",
                "schema": {
                  "type": "string"
                }
              },
              "X-AfricaOne-API-Version": {
                "schema": {
                  "const": "1.0"
                }
              },
              "Cache-Control": {
                "schema": {
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "404": {
            "description": "Fail-closed RFC 9457 Problem Details. No stack traces or credentials.",
            "headers": {
              "X-Request-ID": {
                "description": "Server-generated opaque request correlation ID.",
                "schema": {
                  "type": "string"
                }
              },
              "X-AfricaOne-API-Version": {
                "schema": {
                  "const": "1.0"
                }
              },
              "Cache-Control": {
                "schema": {
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "405": {
            "description": "Fail-closed RFC 9457 Problem Details. No stack traces or credentials.",
            "headers": {
              "X-Request-ID": {
                "description": "Server-generated opaque request correlation ID.",
                "schema": {
                  "type": "string"
                }
              },
              "X-AfricaOne-API-Version": {
                "schema": {
                  "const": "1.0"
                }
              },
              "Cache-Control": {
                "schema": {
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          },
          "503": {
            "description": "Fail-closed RFC 9457 Problem Details. No stack traces or credentials.",
            "headers": {
              "X-Request-ID": {
                "description": "Server-generated opaque request correlation ID.",
                "schema": {
                  "type": "string"
                }
              },
              "X-AfricaOne-API-Version": {
                "schema": {
                  "const": "1.0"
                }
              },
              "Cache-Control": {
                "schema": {
                  "const": "no-store"
                }
              }
            },
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/Problem"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "developerBearer": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "at+jwt",
        "description": "A 300 s EdDSA access token from POST /oauth/token. Audience and environment must match the deployment; the app, developer, client secret and (for user tokens) the consent must all still be active, checked on every call. Scopes are checked per route (x-required-scopes)."
      },
      "clientSecretBasic": {
        "type": "http",
        "scheme": "basic",
        "description": "client_id and client_secret (RFC 6749 §2.3.1); or client_secret_post in the form body."
      }
    },
    "schemas": {
      "Problem": {
        "type": "object",
        "additionalProperties": false,
        "required": ["type", "title", "status", "detail", "instance", "code", "request_id"],
        "properties": {
          "type": {
            "type": "string",
            "format": "uri"
          },
          "title": {
            "type": "string"
          },
          "status": {
            "type": "integer",
            "minimum": 400,
            "maximum": 599
          },
          "detail": {
            "type": "string"
          },
          "instance": {
            "type": "string"
          },
          "code": {
            "type": "string"
          },
          "request_id": {
            "type": "string"
          }
        }
      },
      "Capabilities": {
        "type": "object",
        "additionalProperties": false,
        "required": ["api_version", "environment", "capabilities"],
        "properties": {
          "api_version": {
            "const": "1.0"
          },
          "environment": {
            "enum": ["sandbox", "staging", "production"]
          },
          "capabilities": {
            "type": "object",
            "additionalProperties": {
              "type": "boolean"
            }
          }
        }
      },
      "Status": {
        "type": "object",
        "additionalProperties": false,
        "required": ["api_version", "environment", "status", "production_ready", "platform"],
        "properties": {
          "api_version": {
            "const": "1.0"
          },
          "environment": {
            "enum": ["sandbox", "staging", "production"]
          },
          "status": {
            "enum": ["sandbox", "limited"]
          },
          "production_ready": {
            "const": false
          },
          "platform": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/PlatformStatus"
              },
              {
                "type": "null"
              }
            ]
          }
        }
      },
      "Regions": {
        "type": "object",
        "additionalProperties": false,
        "required": ["api_version", "data", "next_cursor"],
        "properties": {
          "api_version": {
            "const": "1.0"
          },
          "data": {
            "type": "array",
            "maxItems": 100,
            "items": {
              "$ref": "#/components/schemas/Region"
            }
          },
          "next_cursor": {
            "type": "null"
          }
        }
      },
      "City": {
        "type": "object",
        "additionalProperties": false,
        "required": ["id", "name", "country_code", "availability", "supported_capabilities"],
        "properties": {
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "country_code": {
            "type": "string",
            "pattern": "^[A-Z]{2}$"
          },
          "availability": {
            "enum": ["closed", "closed_beta", "available"]
          },
          "supported_capabilities": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "Cities": {
        "type": "object",
        "additionalProperties": false,
        "required": ["api_version", "data", "next_cursor"],
        "properties": {
          "api_version": {
            "const": "1.0"
          },
          "data": {
            "type": "array",
            "maxItems": 100,
            "items": {
              "$ref": "#/components/schemas/City"
            }
          },
          "next_cursor": {
            "type": "null"
          }
        }
      },
      "OAuthError": {
        "type": "object",
        "additionalProperties": false,
        "required": ["error", "error_description"],
        "properties": {
          "error": {
            "enum": [
              "invalid_request",
              "invalid_client",
              "invalid_grant",
              "invalid_scope",
              "unsupported_grant_type",
              "temporarily_unavailable"
            ]
          },
          "error_description": {
            "type": "string"
          }
        }
      },
      "TokenRequest": {
        "type": "object",
        "required": ["grant_type"],
        "properties": {
          "grant_type": {
            "enum": ["client_credentials", "authorization_code", "refresh_token"]
          },
          "client_id": {
            "type": "string",
            "pattern": "^aoc_[a-z2-7]{26}$"
          },
          "client_secret": {
            "type": "string"
          },
          "scope": {
            "type": "string",
            "description": "Space-separated; a subset of what was granted or consented."
          },
          "code": {
            "type": "string",
            "description": "authorization_code: the single-use code from the redirect."
          },
          "redirect_uri": {
            "type": "string",
            "format": "uri",
            "description": "authorization_code: exactly the registered URI used."
          },
          "code_verifier": {
            "type": "string",
            "minLength": 43,
            "maxLength": 128,
            "description": "authorization_code: RFC 7636 verifier (S256)."
          },
          "refresh_token": {
            "type": "string",
            "description": "refresh_token: the current refresh token; it is rotated."
          }
        }
      },
      "TokenResponse": {
        "type": "object",
        "additionalProperties": false,
        "required": ["access_token", "token_type", "expires_in", "scope"],
        "properties": {
          "access_token": {
            "type": "string"
          },
          "token_type": {
            "const": "Bearer"
          },
          "expires_in": {
            "const": 300
          },
          "scope": {
            "type": "string"
          },
          "refresh_token": {
            "type": "string",
            "description": "User grants only. Rotated on every use."
          }
        }
      },
      "RevokeRequest": {
        "type": "object",
        "required": ["token"],
        "properties": {
          "token": {
            "type": "string"
          },
          "token_type_hint": {
            "enum": ["refresh_token", "access_token"]
          },
          "client_id": {
            "type": "string"
          },
          "client_secret": {
            "type": "string"
          }
        }
      },
      "AuthorizationServerMetadata": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "issuer",
          "authorization_endpoint",
          "token_endpoint",
          "revocation_endpoint",
          "response_types_supported",
          "grant_types_supported",
          "code_challenge_methods_supported",
          "token_endpoint_auth_methods_supported",
          "revocation_endpoint_auth_methods_supported",
          "scopes_supported"
        ],
        "properties": {
          "issuer": {
            "type": "string",
            "format": "uri"
          },
          "authorization_endpoint": {
            "type": "string",
            "format": "uri"
          },
          "token_endpoint": {
            "type": "string",
            "format": "uri"
          },
          "revocation_endpoint": {
            "type": "string",
            "format": "uri"
          },
          "response_types_supported": {
            "type": "array",
            "items": {
              "const": "code"
            }
          },
          "grant_types_supported": {
            "type": "array",
            "items": {
              "enum": ["authorization_code", "refresh_token", "client_credentials"]
            }
          },
          "code_challenge_methods_supported": {
            "type": "array",
            "items": {
              "const": "S256"
            }
          },
          "token_endpoint_auth_methods_supported": {
            "type": "array",
            "items": {
              "enum": ["client_secret_basic", "client_secret_post"]
            }
          },
          "revocation_endpoint_auth_methods_supported": {
            "type": "array",
            "items": {
              "enum": ["client_secret_basic", "client_secret_post"]
            }
          },
          "scopes_supported": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "Me": {
        "type": "object",
        "additionalProperties": false,
        "required": ["api_version", "sub", "display_name"],
        "properties": {
          "api_version": {
            "const": "1.0"
          },
          "sub": {
            "type": "string",
            "pattern": "^ps_[A-Za-z0-9_-]{32}$"
          },
          "display_name": {
            "type": ["string", "null"]
          }
        }
      },
      "Region": {
        "type": "object",
        "additionalProperties": false,
        "required": ["code", "name", "region", "cities"],
        "properties": {
          "code": {
            "type": "string",
            "pattern": "^[A-Z]{2}$"
          },
          "name": {
            "type": "string"
          },
          "region": {
            "type": "string"
          },
          "cities": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": ["id", "availability"],
              "properties": {
                "id": {
                  "type": "string"
                },
                "availability": {
                  "enum": ["closed", "closed_beta", "available"]
                }
              }
            }
          }
        }
      },
      "PlatformStatus": {
        "type": "object",
        "additionalProperties": false,
        "required": ["overall", "updated_at", "components"],
        "properties": {
          "overall": {
            "enum": [
              "OPERATIONAL",
              "DEGRADED",
              "PARTIAL_OUTAGE",
              "MAJOR_OUTAGE",
              "MAINTENANCE",
              "UNKNOWN"
            ]
          },
          "updated_at": {
            "type": "string"
          },
          "components": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": false,
              "required": ["code", "status"],
              "properties": {
                "code": {
                  "type": "string"
                },
                "status": {
                  "enum": [
                    "OPERATIONAL",
                    "DEGRADED",
                    "PARTIAL_OUTAGE",
                    "MAJOR_OUTAGE",
                    "MAINTENANCE",
                    "UNKNOWN"
                  ]
                }
              }
            }
          }
        }
      }
    }
  }
}
