Connected Apps / Dynamic Client Registration

Register OAuth 2.0 clients dynamically and manage them via the admin API.


Dynamic Client Registration

POST /oauth2/register

Register a new OAuth 2.0 client following RFC 7591. This endpoint is used by applications that need to become first-class OAuth clients within Bulwark — receiving their own client_id and client_secret — so that agents can authenticate on their behalf.

Headers

  • X-Bulwark-Tenant: <tenant-id>
  • Authorization: Bearer <initial-access-token> — Optional. Required if DCR is restricted by tenant policy.

Body

{
  "client_name": "Acme Data Exporter",
  "redirect_uris": ["https://app.acme.com/auth/callback"],
  "grant_types": ["authorization_code", "refresh_token"],
  "response_types": ["code"],
  "scope": "read:customers write:reports",
  "token_endpoint_auth_method": "client_secret_basic",
  "application_type": "web",
  "contacts": ["[email protected]"],
  "logo_uri": "https://app.acme.com/logo.png",
  "client_uri": "https://app.acme.com"
}

| Field | Required | Description | |-------|----------|-------------| | client_name | Yes | Human-readable application name | | redirect_uris | Yes (for authorization_code) | Allowed redirect URIs | | grant_types | No | Defaults to ["authorization_code"]. Also supports client_credentials. | | response_types | No | Defaults to ["code"] | | scope | No | Requested OAuth scopes | | token_endpoint_auth_method | No | client_secret_basic, client_secret_post, or none |

Response 201

{
  "client_id": "app_01j...",
  "client_secret": "cs_live_...",
  "client_id_issued_at": 1743292800,
  "client_secret_expires_at": 0,
  "client_name": "Acme Data Exporter",
  "redirect_uris": ["https://app.acme.com/auth/callback"],
  "grant_types": ["authorization_code", "refresh_token"],
  "response_types": ["code"],
  "scope": "read:customers write:reports",
  "token_endpoint_auth_method": "client_secret_basic",
  "registration_access_token": "rat_01j...",
  "registration_client_uri": "/oauth2/register/app_01j..."
}

Important: client_secret is returned only once. Store it securely.


List Clients (Admin)

GET /api/v1/oauth2/clients

Headers

  • Authorization: Bearer <apiKey>
  • X-Bulwark-Tenant: <tenant-id>

Query Parameters

| Parameter | Description | |-----------|-------------| | limit | Results per page (default: 20, max: 100) | | cursor | Pagination cursor | | grant_type | Filter by grant type |

Response 200

{
  "data": [
    {
      "client_id": "app_01j...",
      "client_name": "Acme Data Exporter",
      "grant_types": ["authorization_code", "refresh_token"],
      "scope": "read:customers write:reports",
      "created_at": "2026-03-30T00:00:00Z",
      "status": "active"
    }
  ],
  "pagination": { "cursor": "...", "has_more": false }
}

Create Client (Admin)

POST /api/v1/oauth2/clients

Create a client directly without an initial access token. Equivalent to DCR but requires admin credentials.

Headers

  • Authorization: Bearer <apiKey>
  • X-Bulwark-Tenant: <tenant-id>

Body

Same schema as POST /oauth2/register.

Response 201

Same schema as the DCR 201 response.


Revoke Client

DELETE /api/v1/oauth2/clients/{id}

Immediately revokes the client and all tokens issued to it. Active sessions authenticated via this client will be terminated.

Headers

  • Authorization: Bearer <apiKey>
  • X-Bulwark-Tenant: <tenant-id>

Response 200

{
  "data": {
    "client_id": "app_01j...",
    "status": "revoked",
    "revoked_at": "2026-03-30T14:00:00Z"
  }
}