# Build the one-click apply URL (sync) or start the managed async rail

> start domain connect: reference page for this CustomDomain™ API operation in the connections group. Covers parameters, request body, responses and error codes.

Source: https://docs.customdomain.ai/docs/api-reference/connections/startDomainConnect

## `POST https://api.customdomain.ai/v1/connections/{id}/domainconnect:start`

**Rail A (one-click sync-redirect) by default.** `service_id` may be omitted when a default/sole template is loaded; the response carries the provider-hosted `apply_url`, the resolved `service_id`, and the discovered `dc_provider` name. Set `managed: true` (or `rail: "async"`) to request the managed async rail instead. When the provider supports it the response carries a `consent_url` and `rail: "async"` (and the grant lets the control plane re-apply / revert server-side without re-consent). `service_ids` (plural) bundles several templates under one managed consent (async only; the OAuth scope is the space-separated list and the callback applies each); on the sync fallthrough only the first template applies.

Either rail is offered only when the DNS provider holds the exact template: the control plane runs the Domain Connect template check against the provider's API first (remembered for a few minutes), and a provider that has not onboarded it is refused with a `503` that names it. A root domain uses `apex-a` (an `A` record to the edge address, which needs `APEX_A_TARGETS`). Our templates point domains at the edge, so a domain routed to your own infrastructure is refused with `422 custom_routing`. The sync `apply_url` carries a signed `redirect_uri` back to `/connect/dc/return/{state}` whenever the template allows it; pass `return_origin` on a sync start to have that return page post the outcome to your page.

A widget JWT starts either rail 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.

A delegated agent token needs `domains:connect` and starts the setup of its own application's connections.

A widget token must be the connection's end user: a connection that names another end user than the token's `end_user_ref`, or names one while the token names none, answers `403` `token_wrong_end_user` before anything is read or changed, and a connection that names none is any token's. An API key, an app-scoped token and a delegated agent token are not limited by it.

### Authentication

`widgetJWT`, `apiKey`, `agentToken`

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `id` | path | string | yes |  |

### Request body (JSON)

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `service_id` | string | no |  |
| `service_ids` | array of string | no | Bundle of templates to authorize under one managed consent (async rail only). Wins over `service_id` when set. |
| `host` | string | no |  |
| `vars` | object | no | The template's caller-supplied variables, and only those: `email-full` takes `mxHost`, `spfInclude`, `dkimSelector`, `dkimTarget` and `dmarcRua` (`email-mx`, `email-spf`, `email-dkim` and `email-dmarc` take their part of that set), `redirect` takes `redirectTarget`, and the other templates take none. `edgeIp` and `edgeIp6` are set by the server from its configuration; naming one, or a variable the template does not take, answers `400 invalid_request`. Each value must have its variable's shape, else `400 invalid_request` whose `details` names it: `mxHost`, `spfInclude` and `dkimTarget` are public hostnames (not an IP address, `localhost` or an internal name such as `.internal` or `.local`); `dkimSelector` is one DNS label; `dmarcRua` is one email address (a `mailto:` prefix is accepted and dropped); `redirectTarget` is an http or https URL, or a host, at a public hostname. The records of a template that takes caller values are stored as awaiting the DNS provider until public DNS shows them, and are not written by a re-apply or into a hosted zone before then. |
| `managed` | boolean | no | Opt into the managed (async) one-click rail. |
| `rail` | string | no | Set to "async" to request the managed rail (same effect as managed:true). |
| `return_origin` | string | no | Vetted web origin the finish page may postMessage the outcome to; must be on the server allowlist. Used by the async callback, and by the sync return page on a plain sync start. |

### Responses

| Status | Description |
| --- | --- |
| `200` | Sync rail returns `apply_url`; the managed async rail returns `consent_url` and `rail: "async"`. |
| `400` | Error envelope: {code, title, details}. |
| `401` | Error envelope: {code, title, details}. |
| `403` | `forbidden`: the credential may not do this. `workspace_suspended`: see the `ForbiddenOrSuspended` response. `token_wrong_end_user`: a widget token that is not this connection's end user (the connection names another end user than the token's `end_user_ref`, or names one while the token names none); nothing is read, written or changed, and the answer shows nothing of the connection. Mint the token with the end user's `end_user_ref`. |
| `404` | Error envelope: {code, title, details}. |
| `422` | The target is a root (apex) domain that the selected template cannot serve. Branch on `code`: `apex_template_required` means the named template is `hostRequired` (or no apex-capable template is loaded): retry without `service_id`, or pass a `host` to set up a subdomain instead. `InvalidNameservers` means the domain has no usable NS delegation. `custom_routing` means the domain is routed to your own infrastructure (its `routing.mode` is `custom`) and the templates point domains at the CustomDomain™ edge: add the records manually or with a provider API key. |
| `503` | Error envelope: {code, title, details}. |

### `200` response body

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `apply_url` | string | no | Sync rail: provider-hosted apply URL. |
| `consent_url` | string | no | Async rail: provider consent URL. |
| `service_id` | string | no |  |
| `service_ids` | array of string | no | Async rail: the resolved template bundle. |
| `dc_provider` | string | no |  |
| `rail` | string | no | "async" on the managed rail; omitted on the sync rail. |

The full specification is served at `GET https://api.customdomain.ai/v1/openapi.json`.
