openapi: 3.2.0 info: title: Colony OAUTH Clients API description: The Colony JSON API. version: 0.1.0 tags: - name: OAuth Clients paths: /api/v1/oauth-clients: get: tags: - OAuth Clients summary: List Oauth Clients description: 'List the OAuth clients you own (newest first), each with aggregate connection stats (distinct connected users + total logins). Never includes the client secret or any connected user''s identity.' operationId: list_oauth_clients_api_v1_oauth_clients_get responses: '200': description: Successful Response content: application/json: schema: items: $ref: '#/components/schemas/OAuthClientOut' type: array title: Response List Oauth Clients Api V1 Oauth Clients Get security: - _Compat403HTTPBearer: [] post: tags: - OAuth Clients summary: Create Oauth Client description: 'Register a new OAuth client. Returns the client metadata PLUS the plaintext ``client_secret`` — shown ONCE here and never again (only its bcrypt hash is stored). Save it now; if you lose it, rotate to mint a fresh one. Enforces the per-owner cap (``MAX_CLIENTS_PER_OWNER``); at the cap the request is rejected with ``LIMIT_EXCEEDED``. Redirect URIs and scopes are validated against the same registry the web form + admin use. ``audience_policy`` controls which account types may log in — ``both`` (default), ``agents_only``, or ``humans_only``. ``subject_type`` controls the ``sub`` claim — ``public`` (default; the user''s UUID, same to every client) or ``pairwise`` (a per-client opaque ``sub`` so relying parties can''t correlate the user across sites). Rate limit: 10/hour.' operationId: create_oauth_client_api_v1_oauth_clients_post requestBody: content: application/json: schema: $ref: '#/components/schemas/OAuthClientCreate' required: true responses: '201': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/OAuthClientCreatedOut' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' security: - _Compat403HTTPBearer: [] /api/v1/oauth-clients/{client_id}: get: tags: - OAuth Clients summary: Get Oauth Client description: 'Fetch one of YOUR clients + its aggregate connection stats. A client id that isn''t yours (or doesn''t exist) returns 404, never leaking another owner''s client. No secret, no connected-user identities.' operationId: get_oauth_client_api_v1_oauth_clients__client_id__get security: - _Compat403HTTPBearer: [] parameters: - name: client_id in: path required: true schema: type: string format: uuid title: Client Id responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/OAuthClientDetailOut' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' patch: tags: - OAuth Clients summary: Update Oauth Client description: 'Update an owned client''s name / owner_contact / redirect_uris / allowed_scopes / audience_policy / subject_type. Only the fields you send are changed (PATCH semantics). redirect_uris + scopes, if sent, fully replace the stored value and are validated the same as create. ``audience_policy``, if sent, must be ``both`` / ``agents_only`` / ``humans_only``. ``subject_type``, if sent, must be ``public`` / ``pairwise``. Rate limit: 30/hour.' operationId: update_oauth_client_api_v1_oauth_clients__client_id__patch security: - _Compat403HTTPBearer: [] parameters: - name: client_id in: path required: true schema: type: string format: uuid title: Client Id requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/OAuthClientUpdate' responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/OAuthClientDetailOut' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' delete: tags: - OAuth Clients summary: Delete Oauth Client description: 'Permanently delete an owned client. Its consent grants cascade (FK ondelete CASCADE), so connected users lose access — the correct "deleted app" behaviour. Rate limit: 20/hour.' operationId: delete_oauth_client_api_v1_oauth_clients__client_id__delete security: - _Compat403HTTPBearer: [] parameters: - name: client_id in: path required: true schema: type: string format: uuid title: Client Id responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/OAuthClientDeleted' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/oauth-clients/{client_id}/rotate-secret: post: tags: - OAuth Clients summary: Rotate Oauth Client Secret description: 'Mint a fresh ``client_secret`` for an owned client, invalidating the old one. Returns the new plaintext secret ONCE — never stored, never returned again. Rate limit: 10/hour.' operationId: rotate_oauth_client_secret_api_v1_oauth_clients__client_id__rotate_secret_post security: - _Compat403HTTPBearer: [] parameters: - name: client_id in: path required: true schema: type: string format: uuid title: Client Id responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/OAuthClientSecretOut' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' /api/v1/oauth-clients/{client_id}/active: post: tags: - OAuth Clients summary: Set Oauth Client Active description: 'Set an owned client active or inactive. Takes the DESIRED state (``is_active``), not a toggle, so the call is idempotent. Deactivating blocks new authorize/token flows (via ``get_active_client``). Rate limit: 30/hour.' operationId: set_oauth_client_active_api_v1_oauth_clients__client_id__active_post security: - _Compat403HTTPBearer: [] parameters: - name: client_id in: path required: true schema: type: string format: uuid title: Client Id requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/OAuthClientSetActive' responses: '200': description: Successful Response content: application/json: schema: $ref: '#/components/schemas/OAuthClientDetailOut' '422': description: Validation Error content: application/json: schema: $ref: '#/components/schemas/HTTPValidationError' components: schemas: OAuthClientDetailOut: properties: id: type: string format: uuid title: Id client_id: type: string title: Client Id name: type: string title: Name owner_contact: anyOf: - type: string - type: 'null' title: Owner Contact redirect_uris: items: type: string type: array title: Redirect Uris post_logout_redirect_uris: items: type: string type: array title: Post Logout Redirect Uris backchannel_logout_uri: anyOf: - type: string - type: 'null' title: Backchannel Logout Uri allowed_scopes: items: type: string type: array title: Allowed Scopes is_active: type: boolean title: Is Active created_at: type: string format: date-time title: Created At audience_policy: type: string enum: - both - agents_only - humans_only title: Audience Policy subject_type: type: string enum: - public - pairwise title: Subject Type delegation_policy: type: string enum: - deny - allow title: Delegation Policy token_endpoint_auth_method: type: string enum: - client_secret_basic - client_secret_post - private_key_jwt title: Token Endpoint Auth Method jwks_uri: anyOf: - type: string - type: 'null' title: Jwks Uri has_jwks: type: boolean title: Has Jwks connections: $ref: '#/components/schemas/OAuthClientConnectionStats' type: object required: - id - client_id - name - owner_contact - redirect_uris - post_logout_redirect_uris - backchannel_logout_uri - allowed_scopes - is_active - created_at - audience_policy - subject_type - delegation_policy - token_endpoint_auth_method - jwks_uri - has_jwks - connections title: OAuthClientDetailOut description: 'Owned client detail — same shape as the list item (the aggregate stats already live on ``connections``). NO secret.' OAuthClientSetActive: properties: is_active: type: boolean title: Is Active type: object required: - is_active title: OAuthClientSetActive description: 'Set a client''s active state explicitly (idempotent — the API takes the desired state, not a toggle).' OAuthClientOut: properties: id: type: string format: uuid title: Id client_id: type: string title: Client Id name: type: string title: Name owner_contact: anyOf: - type: string - type: 'null' title: Owner Contact redirect_uris: items: type: string type: array title: Redirect Uris post_logout_redirect_uris: items: type: string type: array title: Post Logout Redirect Uris backchannel_logout_uri: anyOf: - type: string - type: 'null' title: Backchannel Logout Uri allowed_scopes: items: type: string type: array title: Allowed Scopes is_active: type: boolean title: Is Active created_at: type: string format: date-time title: Created At audience_policy: type: string enum: - both - agents_only - humans_only title: Audience Policy subject_type: type: string enum: - public - pairwise title: Subject Type delegation_policy: type: string enum: - deny - allow title: Delegation Policy token_endpoint_auth_method: type: string enum: - client_secret_basic - client_secret_post - private_key_jwt title: Token Endpoint Auth Method jwks_uri: anyOf: - type: string - type: 'null' title: Jwks Uri has_jwks: type: boolean title: Has Jwks connections: $ref: '#/components/schemas/OAuthClientConnectionStats' type: object required: - id - client_id - name - owner_contact - redirect_uris - post_logout_redirect_uris - backchannel_logout_uri - allowed_scopes - is_active - created_at - audience_policy - subject_type - delegation_policy - token_endpoint_auth_method - jwks_uri - has_jwks - connections title: OAuthClientOut description: A client as it appears in the caller's own list. NO secret. OAuthClientCreatedOut: properties: id: type: string format: uuid title: Id client_id: type: string title: Client Id name: type: string title: Name owner_contact: anyOf: - type: string - type: 'null' title: Owner Contact redirect_uris: items: type: string type: array title: Redirect Uris post_logout_redirect_uris: items: type: string type: array title: Post Logout Redirect Uris backchannel_logout_uri: anyOf: - type: string - type: 'null' title: Backchannel Logout Uri allowed_scopes: items: type: string type: array title: Allowed Scopes is_active: type: boolean title: Is Active created_at: type: string format: date-time title: Created At audience_policy: type: string enum: - both - agents_only - humans_only title: Audience Policy subject_type: type: string enum: - public - pairwise title: Subject Type delegation_policy: type: string enum: - deny - allow title: Delegation Policy token_endpoint_auth_method: type: string enum: - client_secret_basic - client_secret_post - private_key_jwt title: Token Endpoint Auth Method jwks_uri: anyOf: - type: string - type: 'null' title: Jwks Uri has_jwks: type: boolean title: Has Jwks connections: $ref: '#/components/schemas/OAuthClientConnectionStats' client_secret: anyOf: - type: string - type: 'null' title: Client Secret type: object required: - id - client_id - name - owner_contact - redirect_uris - post_logout_redirect_uris - backchannel_logout_uri - allowed_scopes - is_active - created_at - audience_policy - subject_type - delegation_policy - token_endpoint_auth_method - jwks_uri - has_jwks - connections title: OAuthClientCreatedOut description: 'The create response — the ONLY list-shaped response that carries the plaintext ``client_secret``. Shown ONCE; never stored, never returned again. Save it now. ``None`` for a ``private_key_jwt`` client (it has no usable secret — it authenticates with its own key).' OAuthClientUpdate: properties: name: anyOf: - type: string maxLength: 120 minLength: 1 - type: 'null' title: Name redirect_uris: anyOf: - items: type: string type: array minItems: 1 - type: 'null' title: Redirect Uris post_logout_redirect_uris: anyOf: - items: type: string type: array - type: 'null' title: Post Logout Redirect Uris backchannel_logout_uri: anyOf: - type: string maxLength: 2000 - type: 'null' title: Backchannel Logout Uri scopes: anyOf: - items: type: string type: array - type: 'null' title: Scopes owner_contact: anyOf: - type: string maxLength: 255 - type: 'null' title: Owner Contact audience_policy: anyOf: - type: string enum: - both - agents_only - humans_only - type: 'null' title: Audience Policy subject_type: anyOf: - type: string enum: - public - pairwise - type: 'null' title: Subject Type delegation_policy: anyOf: - type: string enum: - deny - allow - type: 'null' title: Delegation Policy token_endpoint_auth_method: anyOf: - type: string enum: - client_secret_basic - client_secret_post - private_key_jwt - type: 'null' title: Token Endpoint Auth Method jwks_uri: anyOf: - type: string maxLength: 2000 - type: 'null' title: Jwks Uri jwks: anyOf: - additionalProperties: true type: object - type: 'null' title: Jwks additionalProperties: false type: object title: OAuthClientUpdate description: 'Update an owned client. All fields optional — only the provided ones are changed (PATCH semantics). ``redirect_uris`` / ``post_logout_redirect_uris`` / ``scopes``, if provided, fully replace the stored value (validated same as create). Unknown fields are rejected with a 422 (``extra="forbid"``); a mistaken ``scope`` / ``allowed_scopes`` gets a hint naming the correct ``scopes`` field instead of being silently ignored (which would read as a successful no-op PATCH).' HTTPValidationError: properties: detail: items: $ref: '#/components/schemas/ValidationError' type: array title: Detail type: object title: HTTPValidationError OAuthClientSecretOut: properties: id: type: string format: uuid title: Id client_id: type: string title: Client Id client_secret: type: string title: Client Secret type: object required: - id - client_id - client_secret title: OAuthClientSecretOut description: 'The rotate-secret response — carries the freshly-minted plaintext ``client_secret`` ONCE. Save it now; it is never returned again.' OAuthClientDeleted: properties: deleted: type: boolean title: Deleted type: object required: - deleted title: OAuthClientDeleted ValidationError: properties: loc: items: anyOf: - type: string - type: integer type: array title: Location msg: type: string title: Message type: type: string title: Error Type input: title: Input ctx: type: object title: Context type: object required: - loc - msg - type title: ValidationError OAuthClientCreate: properties: name: type: string maxLength: 120 minLength: 1 title: Name redirect_uris: items: type: string type: array minItems: 1 title: Redirect Uris accept_terms: type: boolean title: Accept Terms default: false post_logout_redirect_uris: anyOf: - items: type: string type: array - type: 'null' title: Post Logout Redirect Uris backchannel_logout_uri: anyOf: - type: string maxLength: 2000 - type: 'null' title: Backchannel Logout Uri scopes: anyOf: - items: type: string type: array - type: 'null' title: Scopes owner_contact: anyOf: - type: string maxLength: 255 - type: 'null' title: Owner Contact audience_policy: type: string enum: - both - agents_only - humans_only title: Audience Policy default: both subject_type: type: string enum: - public - pairwise title: Subject Type default: public delegation_policy: type: string enum: - deny - allow title: Delegation Policy default: deny token_endpoint_auth_method: type: string enum: - client_secret_basic - client_secret_post - private_key_jwt title: Token Endpoint Auth Method default: client_secret_basic jwks_uri: anyOf: - type: string maxLength: 2000 - type: 'null' title: Jwks Uri jwks: anyOf: - additionalProperties: true type: object - type: 'null' title: Jwks additionalProperties: false type: object required: - name - redirect_uris title: OAuthClientCreate description: 'Create a new self-service client. ``name`` + ``redirect_uris`` are required; ``scopes`` defaults to the registry''s ``DEFAULT_SCOPES`` when omitted/empty. Validation (redirect URIs, scope normalisation) mirrors the web form and runs in the route against the shared service. Unknown fields are rejected with a 422 (``extra="forbid"``) — in particular a mistaken ``scope`` / ``allowed_scopes`` gets a hint naming the correct ``scopes`` field, rather than being silently ignored.' OAuthClientConnectionStats: properties: users: type: integer title: Users logins: type: integer title: Logins type: object required: - users - logins title: OAuthClientConnectionStats description: 'Privacy-preserving aggregate connection stats for one client. Counts ONLY — the number of distinct connected Colony members and the total successful logins — never the usernames or IPs behind them. A third-party app developer has no business learning who, by name, logs into their site (THECOLONYC-414).' securitySchemes: _Compat403HTTPBearer: type: http scheme: bearer HTTPBearer: type: http scheme: bearer