Custom domains
A custom domain puts Flint-hosted pages on hostnames you own. Two hostnames are available:
| Hostname | Set with | What opens on it |
|---|---|---|
Checkout, such as pay.cedarandstone.com | checkout.custom_domain | Hosted checkout, payment links, and invoice payment pages |
Customer account, such as account.cedarandstone.com | customer_account.presentation.custom_domain | Flint's customer account |
Flint issues and renews the certificate for each hostname. Until a hostname is active, Flint keeps using its own addresses, checkout.withflintpay.com and account.withflintpay.com, so new links never point at a hostname that isn't ready.
If you build your own checkout or customer account, you don't need a custom domain: those pages already run on your domain. See Build your own checkout and Build your own customer account.
Before you start#
Buy the custom domain add-on. It costs $15 a month for one checkout hostname and one customer account hostname, billed through Flint billing. Buy it in the dashboard on the Flint billing page. The API doesn't sell it: setting a hostname without it returns CUSTOM_DOMAIN_SUBSCRIPTION_REQUIRED.
Use a live key with settings.write. Checkout hostnames are live mode only. A sandbox key returns CUSTOM_DOMAIN_LIVE_MODE_REQUIRED, and sandbox checkouts always use Flint's checkout address. To confirm a live setup before you share links, use Open on in the dashboard once the hostname is active: for a checkout hostname it opens one of your active payment links there, and for a customer account hostname it opens the sign-in page.
Pick two different subdomains. Use an exact subdomain of a domain you control, such as pay.cedarandstone.com. Flint rejects apex domains such as cedarandstone.com, wildcards, IP addresses, and withflintpay.com hostnames with INVALID_CUSTOM_DOMAIN. Write internationalized hostnames in Punycode.
Set the hostnames#
Both hostnames are settings at merchant scope. Send one or both:
curl -X PATCH https://api.withflintpay.com/v1/settings \
-H "Authorization: Bearer YOUR_LIVE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"checkout": { "custom_domain": "pay.cedarandstone.com" },
"customer_account": {
"mode": "flint_hosted",
"presentation": { "custom_domain": "account.cedarandstone.com" }
}
}'
The response carries a status object for each hostname, checkout_domain_status and customer_account_domain_status, with the DNS records to publish:
{
"data": {
"checkout": { "custom_domain": "pay.cedarandstone.com" },
"checkout_domain_status": {
"hostname": "pay.cedarandstone.com",
"domain_status": "provisioning",
"status_reason": "ownership_record_missing",
"dns_records": [
{ "dns_record_type": "cname", "name": "pay.cedarandstone.com", "value": "checkout-custom.withflintpay.com" },
{ "dns_record_type": "txt", "name": "_flint-verify.pay.cedarandstone.com", "value": "flint-verify=7f3a9c2e5b8d1f4a6c0e9b2d5a8f1c4e7b0d3a6f9c2e5b8d1f4a7c0e3b6d9f2a" }
],
"last_checked_at": null,
"active_payment_attempt_count": 0
},
"customer_account_domain_status": {
"hostname": "account.cedarandstone.com",
"domain_status": "provisioning",
"status_reason": "ownership_record_missing",
"dns_records": [
{ "dns_record_type": "cname", "name": "account.cedarandstone.com", "value": "account.withflintpay.com" },
{ "dns_record_type": "txt", "name": "_flint-verify.account.cedarandstone.com", "value": "flint-verify=2c8e4a6f0b3d7e1a9c5f2b8d4e0a6c3f9b1d7e5a2c8f4b0d6e3a9c1f7b5d2e8a" }
],
"last_checked_at": null
}
},
"request_id": "req_..."
}
GET /v1/settings returns the same status objects at any time. Neither is present before you set a hostname.
Custom domains are merchant settings, like customer_account. Locations and devices can't override checkout.custom_domain.
Publish the DNS records#
Each hostname needs two records at your DNS provider. Copy the values from dns_records rather than from an example: the CNAME value can differ between hostname types and Flint environments, and the TXT value is specific to your account and hostname.
| Type | Name | Value | Purpose |
|---|---|---|---|
CNAME | The hostname, such as pay.cedarandstone.com | The cname value in dns_records | Sends the hostname's traffic to Flint |
TXT | _flint-verify. plus the hostname | flint-verify= plus a token | Proves your Flint account controls the hostname |
Some DNS providers add your domain to every name automatically. There, enter pay and _flint-verify.pay instead of the full names.
Publish the CNAME as DNS-only. If your DNS provider is also a CDN, such as Cloudflare with proxying on, turn proxying off for this record.
Why Flint asks for a TXT record#
A CNAME shows where a hostname points, not who it belongs to. If a hostname you stopped using still pointed at Flint, another Flint account could claim it and serve its own pages there. Flint checks the TXT record before it starts serving a hostname, and checks it again when a hostname was last used by a different account, so nobody can claim your hostname without publishing a record in your DNS.
The TXT value stays the same for the same hostname, hostname type, and Flint environment on your account, including after you remove it and connect it again. If your hostname was active before Flint required the TXT record, it keeps working; add the record anyway, because changing or reconnecting the hostname needs it.
Read the status#
checkout_domain_status and customer_account_domain_status have the same fields:
| Field | What it tells you |
|---|---|
hostname | The hostname the status describes. |
domain_status | Where the hostname is in setup. See the values below. |
status_reason | Why the hostname isn't active, or that it was removed. Omitted while the hostname is active. |
dns_records | The records to publish, each with dns_record_type (cname or txt), name, and value. After removal, the records you can delete. |
last_checked_at | When Flint last checked the hostname, or null before the first check. |
redirect_expires_at | Only on a removed hostname: when its redirects to Flint's address end. |
payment_method_domain_id | The payment method domain Flint registered for the hostname. |
active_payment_attempt_count | checkout_domain_status only: payments in progress whose checkout started on the hostname. Check it before you remove the hostname. |
domain_status takes these values:
| Value | Meaning |
|---|---|
provisioning | Flint is waiting for the DNS records or issuing the certificate. Buyers use Flint's address. |
active | The hostname serves your pages, and new links use it. |
attention_required | Flint couldn't verify the hostname. Read status_reason. |
inactive | The hostname doesn't serve your pages, most often because the custom domain add-on isn't active. Read status_reason. |
removed | You removed the hostname. Old links redirect to Flint's address until redirect_expires_at. |
status_reason explains what to fix:
| Value | Meaning | What to do |
|---|---|---|
dns_record_missing | Flint can't find a CNAME record at the hostname. | Add the cname record from dns_records. |
dns_record_mismatch | The CNAME points somewhere other than the value in dns_records. | Change the record's value to the one in dns_records. |
ownership_record_missing | Flint can't find the _flint-verify TXT record. | Add the txt record from dns_records. |
ownership_record_mismatch | The TXT record exists with a different value. | Change its value to the one in dns_records. |
caa_record_blocks_certificate | A CAA record on your domain doesn't allow the certificate authority that issues Flint's certificates. | See A CAA record blocks the certificate. |
proxied_by_another_provider | The hostname answers through another CDN or proxy instead of Flint. | Turn off proxying for the CNAME record. |
certificate_pending | The DNS records are correct and Flint is issuing the certificate. | Nothing. The status moves to active when the certificate is ready. |
custom_domains_inactive | The custom domain add-on isn't active. | Buy or renew it on the Flint billing page. |
removed_by_merchant | You removed the hostname. | Delete the DNS records once you no longer need the redirects. |
other | A problem Flint has no specific reason for. | Confirm the records match dns_records, check again, then ask Flint Help. |
New values can appear in status_reason and domain_status. Treat a status_reason you don't recognize as other.
Check again#
Flint rechecks hostnames that aren't active on its own. After you fix a DNS record, you can ask for a check right away:
curl -X POST https://api.withflintpay.com/v1/settings/custom-domains/checkout/validate \
-H "Authorization: Bearer YOUR_LIVE_API_KEY"
Use checkout or customer_account in the path. The request has no body, and the response is the hostname's status:
{
"data": {
"hostname": "pay.cedarandstone.com",
"domain_status": "provisioning",
"status_reason": "certificate_pending",
"dns_records": [
{ "dns_record_type": "cname", "name": "pay.cedarandstone.com", "value": "checkout-custom.withflintpay.com" },
{ "dns_record_type": "txt", "name": "_flint-verify.pay.cedarandstone.com", "value": "flint-verify=7f3a9c2e5b8d1f4a6c0e9b2d5a8f1c4e7b0d3a6f9c2e5b8d1f4a7c0e3b6d9f2a" }
],
"last_checked_at": "2026-10-05T17:04:00Z",
"active_payment_attempt_count": 0
},
"request_id": "req_..."
}
You can check each hostname once every 60 seconds. A sooner request returns 429 CUSTOM_DOMAIN_VALIDATION_RATE_LIMITED with a Retry-After header. Checking a hostname type that has no hostname set, or one you removed, returns 409 CUSTOM_DOMAIN_NOT_CONFIGURED. The dashboard's Check again button calls the same route.
Get notified when a status changes#
If you set hostnames through the API, subscribe to custom_domain.status_changed instead of polling GET /v1/settings.
data.object carries every status field, plus domain_type (checkout or customer_account) and previous_status:
{
"event_type": "custom_domain.status_changed",
"data": {
"object": {
"domain_type": "checkout",
"previous_status": "provisioning",
"hostname": "pay.cedarandstone.com",
"domain_status": "active",
"dns_records": [
{ "dns_record_type": "cname", "name": "pay.cedarandstone.com", "value": "checkout-custom.withflintpay.com" },
{ "dns_record_type": "txt", "name": "_flint-verify.pay.cedarandstone.com", "value": "flint-verify=7f3a9c2e5b8d1f4a6c0e9b2d5a8f1c4e7b0d3a6f9c2e5b8d1f4a7c0e3b6d9f2a" }
],
"last_checked_at": "2026-10-05T17:09:00Z",
"payment_method_domain_id": "pmdom_1kmn0aExample",
"active_payment_attempt_count": 0
}
}
}
Flint sends the event whenever domain_status changes, including to removed. A check that changes only last_checked_at sends nothing. When a hostname stops working, or works again, Flint also emails your account owners and shows a notice in the dashboard to team members who can administer the account.
Apple Pay and Google Pay#
Flint registers each hostname as a payment method domain for you, so Apple Pay and Google Pay buttons work on it. payment_method_domain_id identifies the registration; read it with GET /v1/payment-method-domains/{payment_method_domain_id}. Don't register the hostname yourself first.
Redirects during payment#
Affirm, 3D Secure, and other payment steps that leave checkout bring the buyer back to the hostname where the checkout started. A checkout that started on Flint's address returns there. If a hostname stops working while a buyer is paying, that buyer returns to Flint's checkout address and finishes the same checkout there.
Remove a hostname#
Send null to remove a hostname:
curl -X PATCH https://api.withflintpay.com/v1/settings \
-H "Authorization: Bearer YOUR_LIVE_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "checkout": { "custom_domain": null } }'
For the customer account hostname, send "customer_account": { "presentation": { "custom_domain": null } }. An empty string still removes the customer account hostname, but it is deprecated; for checkout.custom_domain an empty string returns INVALID_CUSTOM_DOMAIN. Removing when no hostname is set changes nothing, so a retry is safe. In merchant_hosted mode the account has no hostname to remove, and sending presentation returns CUSTOMER_ACCOUNT_MODE_CONFLICT.
The status changes to removed:
{
"checkout_domain_status": {
"hostname": "pay.cedarandstone.com",
"domain_status": "removed",
"status_reason": "removed_by_merchant",
"dns_records": [
{ "dns_record_type": "cname", "name": "pay.cedarandstone.com", "value": "checkout-custom.withflintpay.com" },
{ "dns_record_type": "txt", "name": "_flint-verify.pay.cedarandstone.com", "value": "flint-verify=7f3a9c2e5b8d1f4a6c0e9b2d5a8f1c4e7b0d3a6f9c2e5b8d1f4a7c0e3b6d9f2a" }
],
"last_checked_at": null,
"redirect_expires_at": "2026-11-04T18:00:00Z",
"active_payment_attempt_count": 0
}
}
After removal:
- New links use Flint's address right away.
- For 30 days, until
redirect_expires_at, any request to the removed hostname redirects to Flint's address. A checkout hostname keeps the same path and query, and a customer account hostname opens the same page of your account on Flint's account address. Shared checkout and payment links, printed QR codes, invoice links, and account links keep working this way. If you delete the CNAME record first, the redirects end when the record does. - The removed hostname serves no pages, only redirects.
- Buyers in the middle of a payment on the hostname finish on Flint's checkout address and are not charged twice. Read
active_payment_attempt_countbefore you remove a checkout hostname to see how many there are. - Delete the records listed in
dns_recordsonce you no longer need the redirects. After the window ends, links on the hostname stop working.
To connect the same hostname again within 30 days, set it again. If its records are still published, it doesn't need new DNS records.
Replace a hostname#
Setting a different hostname replaces the old one. The old hostname is taken off the same way a removal does it: its links redirect to Flint's address for 30 days, it serves no pages, and buyers in the middle of a payment finish on Flint's checkout address. The new hostname starts at provisioning with its own dns_records, and new links use Flint's address until it's active. Delete the old hostname's records once you no longer need its redirects.
If the add-on ends#
Your custom hostnames depend on the custom domain add-on:
- You cancel it. Your hostnames keep working until the end of the month you paid for, then stop.
- A renewal charge fails. Your hostnames keep working while Flint retries, for 24 hours after the paid month ends. If the charge still fails, they stop.
A stopped hostname reports domain_status inactive with status_reason custom_domains_inactive. New links use Flint's addresses, and shared links on the hostname stop working until you buy the add-on again. Buy it again within 30 days and Flint restores every hostname you still have set, without new DNS records. A hostname you removed doesn't come back on its own: set it again.
DNS problems#
Check what resolvers see before you check again in Flint:
dig +short CNAME pay.cedarandstone.com
dig +short TXT _flint-verify.pay.cedarandstone.com
The first command should print the cname value from dns_records and the second the txt value.
A CAA record blocks the certificate#
A CAA record lists the certificate authorities allowed to issue certificates for a domain and its subdomains. If your domain has CAA records that don't allow the authority that issues Flint's certificates, the certificate can't be issued or renewed and status_reason is caa_record_blocks_certificate. Remove the CAA records that apply to the hostname, or ask Flint Help which authority to allow and add a record for it.
The CNAME is proxied#
If the CNAME is proxied through your own Cloudflare account (the orange cloud) or another CDN, requests reach that CDN instead of Flint and status_reason is proxied_by_another_provider. Set the record to DNS-only.
The change hasn't reached resolvers yet#
Resolvers keep an old answer until its TTL runs out, so a new or changed record can take time to appear everywhere. If dig still prints the old value, wait for the TTL and check again. When dig prints the right values, use Check again.
What a custom domain doesn't change#
- Email sender. Flint's emails to buyers still come from Flint's sending address. Links inside them use your hostnames once they're active.
- The Flint mark. The Powered by Flint credit stays on hosted pages unless your agreement with Flint removes it.
- Shipment tracking. Tracking links in shipping notices open the shipment's tracking URL.
- Sign-in. Buyers sign in again on their first visit to your customer account hostname. Browser sessions don't move between Flint's address and yours.
- Sandboxes. Sandbox checkouts use Flint's checkout address.
Not available#
| Request | What to do instead |
|---|---|
| Emails sent from your own domain | Turn off a family of Flint's emails and send your own from the webhook events. See Customer email delivery. |
Apex domains such as cedarandstone.com | Use a subdomain such as pay.cedarandstone.com, and keep your storefront on the apex. |
| Wildcards, or more than one hostname for checkout or for the customer account | Set one hostname of each kind. To serve checkout on more of your domains, build your own checkout. |
| Checkout custom domains in a sandbox | Build and test against Flint's checkout address in a sandbox, then confirm the live hostname with Open on in the dashboard. |
Errors#
Setting a hostname can return:
The error's param is the field you sent, such as checkout.custom_domain. If you use an Idempotency-Key, keep it when you retry MERCHANT_ACCOUNT_NOT_READY, PAYMENT_METHOD_DOMAIN_OPERATION_IN_PROGRESS, or PAYMENT_METHOD_DOMAIN_REGISTRATION_FAILED. A retry with the same key returns CUSTOM_DOMAIN_NOT_VERIFIED again, so once the registration's validation_status is active, send a new key when you set the hostname again.
Checking again can return CUSTOM_DOMAIN_SUBSCRIPTION_REQUIRED and:
Next steps#
- Customer accounts: branding, sign-in, and what the account hostname serves.
- Apple Pay and Google Pay setup: payment method domains and wallet readiness.
- Webhook event payloads: every event Flint sends.
- Flint billing: how Flint charges for the add-on.
- Settings API reference: every settings field.
