# Dynamic client registration

```
POST https://www.bookwithhaven.com/api/public/oauth/register
```

RFC 7591. An unauthenticated endpoint that mints a `client_id` on the spot, for clients that have no other way to obtain one.

**This is for MCP clients.** If you are building a server-side integration against the REST API, do not use it — apply at [Getting access](../00-start-here/01-getting-access.md) and a Haven staffer will issue you a reviewed client with a scope ceiling chosen for what you actually do. That path is unchanged and it is the one that gets you a named support contact, a rate-limit tier and a listing.

Use this endpoint when your software is handed a Haven MCP URL by its own user and has nowhere to get a `client_id` from. That is Claude, ChatGPT, an editor, the MCP Inspector, and anything else built on the MCP SDK's auth module — all of which discover this endpoint from the authorization-server metadata and call it without being told to.

## The request

```http
POST /api/public/oauth/register HTTP/1.1
Content-Type: application/json

{
  "client_name": "Example Assistant",
  "redirect_uris": ["https://example.com/oauth/callback"],
  "token_endpoint_auth_method": "none",
  "grant_types": ["authorization_code", "refresh_token"],
  "response_types": ["code"],
  "client_uri": "https://example.com",
  "policy_uri": "https://example.com/privacy",
  "tos_uri": "https://example.com/terms",
  "software_id": "example-assistant",
  "software_version": "2.4.0"
}
```

`redirect_uris` is the only required field. Everything else has a default, and the four `*_uri` fields must each be an absolute `https` URL if you send them at all.

Redirect URIs go through exactly the same validator a staff-registered client's do — see [The authorize endpoint](02-authorize-endpoint.md) for the full set of rules. In short: `https` only, except `http://127.0.0.1:<port>` and `http://[::1]:<port>` for a native app binding a loopback port; no fragment, no wildcard, no userinfo, no `?next=`-style forwarding parameter; and no custom scheme at all if you ask for a client secret. At most ten.

`token_endpoint_auth_method` may be `none`, `client_secret_basic` or `client_secret_post`. **Send `none` unless your client genuinely runs on a server you control.** PKCE with S256 is mandatory on this API for every client including confidential ones, so `none` is not the weaker option — it is the honest one for anything that ships to a user's machine.

## The response

```http
HTTP/1.1 201 Created
Content-Type: application/json
Cache-Control: no-store

{
  "client_id": "hvci_9pQ2vX7mKd0bR4tYuN1sLa",
  "client_id_issued_at": 1772668800,
  "client_name": "Example Assistant",
  "redirect_uris": ["https://example.com/oauth/callback"],
  "grant_types": ["authorization_code", "refresh_token"],
  "response_types": ["code"],
  "token_endpoint_auth_method": "none",
  "scope": "account:read calendar:read calendar:write …"
}
```

A `client_secret` and a `client_secret_expires_at` of `0` are included when you asked for a secret-bearing auth method. The secret is shown **once**; Haven stores only its hash.

`scope` is the **ceiling** — every scope this client is permitted to ask for at `/oauth/authorize`. It is the full grantable set, because no human chose a narrower one for you. It is not what you get: what you get is decided by the host on the consent screen and reported back in the `scope` field of the token response. Read that field, not this one.

**Store the `client_id` and reuse it.** Registering again mints a second, unrelated client, and a host who already connected the first will be asked to connect the second from scratch.

## What a registered client can and cannot do

Registration mints an identifier. It grants nothing.

A dynamically registered client can complete the [authorization flow](01-authorization-flow.md) immediately — there is no review queue and no waiting — and everything it can then do is bounded by a Haven host signing in and clicking "allow" on a screen that says, in as many words, **that Haven has not reviewed the application**. That is the trust decision, and self-registration moves it to the host rather than removing it.

Two consequences worth designing for:

- **The connection is capped by the person who authorized it, not by what you asked for.** A host whose own role on the account is Viewer grants you a read-only connection: the write scopes are dropped, the consent screen tells them so by name, and the token response reports the narrower set. Read `scope` on the token response and `capabilities.canWrite` on `GET /me`, and render accordingly.
- **A host can narrow or disconnect at any time** from their Haven account, and effective scopes are recomputed on every request. `tools/list` and `GET /operations` are the fastest way to see it.

## Errors

RFC 7591 §3.2.2 shape, at HTTP 400:

```json
{
  "error": "invalid_redirect_uri",
  "error_description": "\"http://example.com/cb\" cannot be registered: only https is allowed, except http on 127.0.0.1 or [::1] for a native application."
}
```

| `error`                   | Means                                                                                                                                    |
| ------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
| `invalid_redirect_uri`    | A URI in `redirect_uris` failed validation, or there were none, or there were more than ten. The description names the URI and the rule. |
| `invalid_client_metadata` | Any other field was the wrong type, too long, not `https`, or asked for a grant type this server does not implement.                     |

Registration is rate-limited by IP: twenty per hour, answered with `429` and a `Retry-After`. A real client registers once and reuses the `client_id` forever, so this is only reachable by a loop. If you hit it while developing, you are re-registering where you should be caching.

## What Haven does not implement

**RFC 7592 client configuration management.** No `registration_access_token` and no `registration_client_uri` come back, so there is no `GET`, `PUT` or `DELETE` on a registered client. Those fields are optional in RFC 7591 and no MCP host uses them; issuing a management credential Haven does not honour would advertise a lifecycle that does not exist. Change your registration by registering again. Retire a connection by having the host disconnect it, or by [revoking your token](05-revoking-and-disconnects.md).

**`software_statement`.** A signed assertion is only worth reading if a key is on file, and nothing signs one today. Send it and it is ignored.
