A form’s share link normally lives on the fomr host. With a custom domain, it can live on your own domain or on a fomr subdomain, under a path you choose. Domains are added and verified in the app, as described in Custom domains. The Platform API lists them and binds them to forms.
| Operation | What it does | Permission |
|---|---|---|
GET / | Lists the domains of the organization. | domains:read |
PUT / | Sets the custom domain and slug of the share link. | forms:write and domains:write |
DELETE / | Removes the custom domain from the share link. | forms:write and domains:write |
Listing domains
Section titled “Listing domains”curl -s -H "Authorization: Bearer $FOMR_TOKEN" \ https://platform.fomr.io/v1/domains{ "data": [ { "host": "acme.fomr.io", "status": "verified", "is_active": true, "is_fomr_subdomain": true, "created_at": "2026-09-14T08:30:00.000Z" }, { "host": "forms.acme.com", "status": "verified", "is_active": true, "is_fomr_subdomain": false, "created_at": "2026-09-20T12:00:00.000Z" }, { "host": "events.acme.com", "status": "pending", "is_active": true, "is_fomr_subdomain": false, "created_at": "2026-10-20T16:45:00.000Z" } ]}Every field of a domain is listed in the response of List domains.
Only a domain whose status is verified and whose is_active is true can be bound to a form. A domain turns inactive when the organization’s plan no longer includes custom domains.
Setting a form’s domain
Section titled “Setting a form’s domain”curl -s -X PUT "https://platform.fomr.io/v1/forms/Ev3ntReg01/domain" \ -H "Authorization: Bearer $FOMR_TOKEN" \ -H "Content-Type: application/json" \ -d '{"domain":"forms.acme.com","slug":"/register"}'The response is the form resource. Its domain block shows the binding, and urls.share uses it once a version is published.
domainis the host fromGET /v1/domains, matched without regard to case.slugis the path after the host. Trailing slashes are removed and a leading slash is added, the same normalization the Share link tab applies, soregister/is stored as/register. Send/to serve the form at the root of the domain. Left out, the slug is/{form_public_id}.- A slug is unique per domain. A slug another form already uses on the same domain is
400 INVALID_INPUTatslug. - Sending the current binding changes nothing and answers
200, so the call is safe to retry. - Setting a domain replaces the previous one. A form has at most one custom domain.
| Refusal | Answer |
|---|---|
| The organization’s plan has no custom domains | 402 PRO_FEATURE_REQUIRED with feature: "domains" |
| The host is unknown, not verified, or inactive | 400 INVALID_INPUT at domain |
The slug holds characters other than letters, digits, -, _, and / | 400 INVALID_INPUT at slug |
| The resource is a Private Template, which has no share link | 400 INVALID_INPUT at domain |
| The form is archived | 409 FORM_ARCHIVED |
The member lacks domains:write | 403 FORBIDDEN |
The binding is stored the way the app stores it, so the editor shows it at once and publishing checks it like any other custom domain.
Removing a form’s domain
Section titled “Removing a form’s domain”DELETE /v1/forms/{form_public_id}/domain removes the binding and answers the form resource, which uses the fomr link again. A form without a custom domain answers 200 and writes nothing. Removing a domain needs no plan feature.