> ## Documentation Index
> Fetch the complete documentation index at: https://docs.whirl.sh/llms.txt
> Use this file to discover all available pages before exploring further.

# Client registration

> Metadata documents, dynamic registration, redirect rules and signing keys.

## Client ID Metadata Document

Host a JSON document over HTTPS at the exact URL you use as `client_id`. It must include your app's name, its `redirect_uris`, and how it authenticates at the token endpoint. Whirl fetches and validates the document.

Metadata documents support `none` and `private_key_jwt`. You can allow both with `token_endpoint_auth_methods_supported`. To use `private_key_jwt`, publish a `jwks_uri` over HTTPS. Whirl only accepts the methods you list. If an assertion is rejected, the request fails; Whirl won't retry it as a public client.

```json theme={null}
{
  "client_id": "https://client.example.com/oauth/client.json",
  "client_name": "Example server-side client",
  "redirect_uris": ["https://client.example.com/oauth/callback"],
  "token_endpoint_auth_methods_supported": ["none", "private_key_jwt"],
  "jwks_uri": "https://client.example.com/oauth/jwks.json"
}
```

If your app can't keep a private key secret, as with mobile, desktop and single-page apps, use `none` with PKCE. Only list `private_key_jwt` if your app supports it.

## Dynamic Client Registration

Post your metadata as JSON to the registration endpoint:

```bash theme={null}
curl --fail-with-body "https://api.whirl.sh/oauth/register" \
  -H "Content-Type: application/json" \
  --data '{
    "client_name": "Example public client",
    "redirect_uris": ["https://client.example.com/oauth/callback"],
    "token_endpoint_auth_method": "none"
  }'
```

Use the `client_id` it returns for sign-in and token requests. Server-side apps can choose `client_secret_basic`, `client_secret_post` or `private_key_jwt`. For `private_key_jwt`, send **either** an inline `jwks` with your public keys **or** a `jwks_uri`, not both. Never send your private key.

## Redirect URIs

Whirl checks the redirect URI before sending the user back. Web apps must use an exact HTTPS URL they registered. Native apps can also use `http` on a loopback address, or a private-use URI scheme. Loopback redirects can use any port, but the host, path and query must match. Fragments, non-loopback `http`, wildcards and some schemes are rejected.

## Fetching your metadata

Metadata documents and key sets must be on public HTTPS. Whirl doesn't follow redirects, and won't fetch from localhost or private network addresses.

Metadata documents can be up to 10 KiB and must load within three seconds. Whirl caches them, so changes can take a while to apply. Key sets have [their own limits and caching](/authentication/private-key-jwt).

## Nothing to set up in Whirl

Your app registers itself with one of the methods above, and users approve it when they connect. **Settings → API & MCP** shows the apps a user has connected.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.