---
title: "Begin the sign-in to the DNS provider for a connection"
method: POST
path: "/connections/{id}/oauth:start"
tags: ["Connections"]
---

# Begin the sign-in to the DNS provider for a connection

`POST /connections/{id}/oauth:start`

## Request

- Base URL: `https://api.customdomain.ai/v1`
- URL: `https://api.customdomain.ai/v1/connections/{id}/oauth:start`
- Auth: one of:
  - HTTP bearer
  - HTTP bearer

**Rail C (flagship).** Returns the URL the widget opens in a popup, bound to an HMAC-signed single-use `state`. The provider redirects back to `/v1/connect/oauth/callback`, which writes the connection's records with what the provider handed over. `flow` says which sign-in runs: `oauth2` (authorization code with PKCE S256; for Google Cloud DNS, Sign in with Google Cloud, whose callback first finds the Google Cloud project that holds the domain's public zone; for Azure DNS, a Microsoft sign-in whose callback first finds the Azure subscription and resource group that hold it), `vercel_integration` (the CustomDomain™ integration's install page; the user picks the Vercel team that owns the domain), `porkbun_handoff` (Porkbun's PKCE API key handoff: the user approves a key named after the domain; no client registration is needed), `ovh_consent` (an OVHcloud consumer key limited to the domain's DNS zone, validated on OVHcloud's page for the chosen `region`) or `mcp_oauth` (OAuth through the provider's hosted MCP server, for Hostinger, Unstoppable Domains, XServer, Namecheap, Spaceship and Dynadot: the endpoints are discovered from the server's metadata, the client is one the control plane registered for itself, and the records are written through the server's DNS tools; offered only for the providers the deployment lists in `MCP_SIGNIN_PROVIDERS`). The credential is used for that write and dropped, unless the deployment keeps grants for this provider (`OAUTH_MANAGED_PROVIDERS` with `GRANT_ENCRYPTION_KEY`), in which case `managed` is true and it is stored encrypted for `:reapply`.

A widget JWT starts the sign-in for its own application's connections (a domain-bound token for its own domain only); an API key for a connection of any application in its tenant, and a member's key needs a role that operates apps.

## Path parameters

- `id` string, required

## Request body

- object
  - `provider` string: DNS provider id; defaults to the detected provider.
  - `return_origin` string, required: Web origin the callback page may postMessage the outcome to; must be on the server's allowlist.
  - `region` 'eu' | 'ca' | 'us': OVHcloud only: the API region the user's OVHcloud account lives in (default `eu`). The domain check's `rails.oauth.regions` lists the regions this deployment offers.
  - `purpose` 'connect' | 'remove': `remove` signs in once more to remove the records we added (and put back what they replaced) for a connection whose access we did not keep, then disconnects the domain, as `GET /connections/{id}/removal` offers when its `record_removal` is `reauthorize`. The sign-in is used for the removal and withdrawn; it is never kept. The callback's message carries `purpose: "remove"` and the `records` outcome, and `GET /connections/{id}/removal` answers it once the domain is gone. An end user's widget token must be bound as for `DELETE /connections/{id}`.
  - `access` 'continuous' | 'one_time': The person's choice for a connect. `continuous` keeps the sign-in, encrypted, so we can put back records that disappear and remove them when the domain is removed; offered only where the deployment can keep this provider's sign-in (`rails.oauth.managed` on the domain check). `one_time` uses it for this write and drops it. Omitted, the deployment's default applies (kept where it can be).

## Response `200`

The URL to open, and what it runs

- object
  - `authorize_url` string, uri, required
  - `provider` string, required
  - `flow` 'oauth2' | 'vercel_integration' | 'porkbun_handoff' | 'ovh_consent' | 'mcp_oauth', required
  - `managed` boolean: The connect will keep its credential (managed mode).
  - `access` 'continuous' | 'one_time': What will happen to the credential after the sign-in.
  - `purpose` 'remove': Present for a removal sign-in.
  - `callback_origin` string: Origin the result will be postMessaged from; check `MessageEvent.origin` against it.
  - `region` string: OVHcloud only, the region the consent was requested in.

## Other responses

- `400`: Error envelope: {code, title, details}.
- `401`: Error envelope: {code, title, details}.
- `404`: Error envelope: {code, title, details}.
- `429`: `provider_rate_limited`: the provider refused to start another sign-in from our servers for now (Porkbun allows 20 key requests an hour per IP address); `details` says when to retry.
- `502`: `provider_unavailable`: the provider did not start the sign-in (Porkbun key request, OVHcloud consumer key request).
- `503`: Error envelope: {code, title, details}.

---

[API](https://skmtc.dev/customdomain/apis/customdomain-control-plane-api.md) · [All operations](https://skmtc.dev/customdomain/apis/customdomain-control-plane-api/llms.txt) · [OpenAPI document](https://skmtc.dev/customdomain/apis/customdomain-control-plane-api/revisions/34d89cb7a7cd?raw)
