---
title: "Create or overwrite a custom DNS record (upsert)"
method: POST
path: "/v1/domains/{domain_id}/custom-dns-records"
tags: ["Domains"]
---

# Create or overwrite a custom DNS record (upsert)

`POST /v1/domains/{domain_id}/custom-dns-records`

## Request

- Base URL: `https://api.winnr.app`
- URL: `https://api.winnr.app/v1/domains/{domain_id}/custom-dns-records`
- Auth: HTTP bearer

Create a TXT, CNAME, A, or AAAA record on the domain's DNS zone.
The request is validated against Winnr-managed records — attempts
to create records that collide with MX, inbound A, DKIM, or SPF
return 409 `managed_record_collision`. Upsert semantics: if a
tracked custom record already exists at the same (name, type), it
is overwritten in place (same record id, new value/ttl) and the
response sets `overwrote_existing: true`. If the zone holds an
untracked record at the same (name, type) (e.g. a tracking CNAME
created at provision time), it is adopted: overwritten in place
and added to the domain's custom records. To change DMARC, use
PUT /v1/domains/{id}/dmarc.

## Path parameters

- `domain_id` string, required

## Request body

- object
  - `name` string, required: "@" for apex, otherwise short label (e.g. "www") or FQDN
  - `type` 'TXT' | 'CNAME' | 'A' | 'AAAA', required
  - `value` string, required
  - `ttl` integer

## Response `200`

Record created (or overwritten in place)

- object
  - `meta` Meta
    - `request_id` string
    - `timestamp` string, date-time
  - `data` object
    - `record` CustomDnsRecord: A user-managed DNS record on a domain's zone. These are validated to never collide with the records Winnr manages for mail delivery (MX, inbound A, DKIM, SPF base, tracking CNAMEs). DMARC is handled via the dedicated `/dmarc` endpoint.
      - `id` string: Winnr-generated UUID for this record
      - `name` string: "@" for apex, otherwise a relative label (e.g. "www")
      - `type` 'TXT' | 'CNAME' | 'A' | 'AAAA'
      - `value` string
      - `ttl` integer
      - `provider_record_id` string: The DNS provider's native record identifier. For Cloudflare and CloudDNS this is the API-returned record ID. For Route 53 (which has no record ID) this is a synthetic `r53:{type}:{fqdn}` string.
      - `created_at` string, date-time
      - `updated_at` string, date-time
      - `created_by` string: Firebase UID that created the record
    - `overwrote_existing` boolean: true when an existing record at the same (name

## Other responses

- `400`: Invalid input (apex CNAME
- `401`: Authentication required or token invalid
- `404`: Resource not found
- `409`: Record collides with a Winnr-managed record
- `503`: DNS provider does not support API record management

---

[API](https://skmtc.dev/winnr/apis/winnr-api.md) · [All operations](https://skmtc.dev/winnr/apis/winnr-api/llms.txt) · [OpenAPI document](https://skmtc.dev/winnr/apis/winnr-api/revisions/a4f960beb1eb?raw)
