domainee-dev Domains API

Connect and verify your customers' custom domains.

Operations 9

GET /v1/domains List connected domains #
POST /v1/domains Connect a custom domain #
GET /v1/domains/{id} Retrieve one domain #
PATCH /v1/domains/{id} Update a domain's origin or routing options #
DELETE /v1/domains/{id} Disconnect a domain #
POST /v1/domains/{id}/check Re-run DNS and SSL checks now #
GET /v1/domains/{id}/instructions DNS steps for the customer's actual DNS provider #
POST /v1/domains/{id}/connect-session Mint a browser-safe token for the embeddable widget #
POST /v1/domains/{id}/dns/apply Point a domain using the customer's own scoped DNS token #

Work with this as data

Every API here is available over the APIs.io API and to AI agents over MCP.

MCP server

One button, every client — Claude, Cursor, VS Code and the rest.

https://apis.io/mcp

Tools for apis

7 MCP tools reach this
  • find_apisBrowse and filter every API in the catalog.
  • get_api_artifactsOne API's artifacts, grouped by type.
  • get_openapiThe primary OpenAPI for this API.
  • find_similar_apisAPIs that look like this one.
  • apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.
  • resolveTurn a domain, URL or GitHub org into the provider it belongs to.
  • find_cohortsEvery scored population of providers in the catalog.
All 92 tools →

Call it yourself

curl for this page
This API
curl "https://apis.io/api/v1/apis/domainee-dev:domainee-dev-domains-api"
All apis
curl "https://apis.io/api/v1/apis?limit=25"

Discovery needs no key. Ratings and market analysis are Pro.

Get an API key

Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.

A second provider on the same verified email joins the account you already have.

OpenAPI Specification

domainee-dev-domains-api-openapi.yml Raw ↑
openapi: 3.2.0
info:
  title: Domainee Domains API
  version: 1.0.0
  summary: Custom domains, domain registration, and DNS diagnostics for SaaS products.
  description: 'Domainee lets your product attach your customers'' own domains to their accounts:

    one call to connect a hostname, automatic SSL issuance and renewal, DNS health

    monitoring, webhooks, and domain registration across 500+ TLDs.


    ## Authentication


    Every `/v1` endpoint except `/v1/tools/*` takes a Bearer token in the

    `Authorization` header. Two kinds work interchangeably:


    - **API keys** (`sk_live_…`), created at https://domainee.dev/developers

    - **OAuth access tokens** (`dmn_oat_…`), issued by the flow described in

    the `/.well-known/oauth-authorization-server` document.


    Both are workspace-scoped: a token only ever sees its own workspace''s data.


    ## Rate limits


    Authenticated endpoints allow 60 requests per minute per key and answer `429`

    with `{"error":"rate_limited"}` beyond that. The free tools are limited per IP

    at 30/minute and 500/day.


    ## Idempotency


    Send an `Idempotency-Key` header on any POST to make a retry safe: the first

    response for a given key is replayed instead of performing the work twice.


    ## Errors


    Failures return the same envelope at every status code:

    `{ "error": "not_found", "message": "Domain not found" }`.


    Full guides: https://domainee.dev/docs. Machine-readable index: https://domainee.dev/llms.txt.'
  termsOfService: https://domainee.dev/terms
  contact:
    name: Domainee support
    email: hello@domainee.dev
    url: https://domainee.dev/contact
servers:
- url: https://api.domainee.dev
  description: Production
security:
- bearerAuth: []
tags:
- name: Domains
  description: Connect and verify your customers' custom domains.
paths:
  /v1/domains:
    get:
      operationId: listDomains
      summary: List connected domains
      description: Cursor-paginated and workspace-scoped. Pass `hostname` for an exact-match lookup instead of paging the whole workspace.
      tags:
      - Domains
      parameters:
      - name: hostname
        in: query
        required: false
        description: Exact hostname to look up. Returns at most one domain and a null `nextCursor`.
        schema:
          type: string
          example: app.acme.com
      - name: status
        in: query
        required: false
        description: Only return domains in this verification state.
        schema:
          type: string
          enum:
          - pending
          - verified
          - failed
          - expired
      - name: cursor
        in: query
        required: false
        description: The `nextCursor` from a previous page.
        schema:
          type: string
      - name: limit
        in: query
        required: false
        description: Page size.
        schema:
          type: integer
          minimum: 1
          maximum: 100
          default: 25
      responses:
        '200':
          description: A page of domains.
          content:
            application/json:
              schema:
                type: object
                properties:
                  domains:
                    type: array
                    items:
                      $ref: '#/components/schemas/Domain'
                  nextCursor:
                    type:
                    - string
                    - 'null'
                    description: Null on the last page.
                required:
                - domains
                - nextCursor
        '401':
          $ref: '#/components/responses/Unauthorized'
        '429':
          $ref: '#/components/responses/RateLimited'
    post:
      operationId: createDomain
      summary: Connect a custom domain
      description: 'Registers the hostname, runs a preflight check, and returns the DNS records

        the domain owner has to create. The domain stays `pending` until those records

        resolve; SSL is issued automatically once they do.


        Requires an active subscription on the workspace even while inside the free

        tier, so a card must be on file.'
      tags:
      - Domains
      parameters:
      - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                hostname:
                  type: string
                  minLength: 1
                  maxLength: 253
                  description: The customer's hostname. Normalized and validated server-side.
                  example: app.acme.com
                originUrl:
                  type: string
                  format: uri
                  description: Where verified traffic should go.
                  example: https://acme.vercel.app
                metadata:
                  type: object
                  additionalProperties: true
                mode:
                  type: string
                  enum:
                  - proxy
                  - redirect
                  default: proxy
                keepHost:
                  type: boolean
                redirectWww:
                  type: boolean
                redirectStatus:
                  type: integer
                  enum:
                  - 301
                  - 302
              required:
              - hostname
              - originUrl
      responses:
        '201':
          description: Domain created. Follow `dnsRecords` to finish verification.
          content:
            application/json:
              schema:
                type: object
                properties:
                  domain:
                    $ref: '#/components/schemas/Domain'
                  warnings:
                    type: array
                    items:
                      $ref: '#/components/schemas/PreflightWarning'
                    description: Non-blocking preflight notes, e.g. an existing CAA record.
                required:
                - domain
        '400':
          description: Invalid hostname, reserved hostname, or a blocking preflight failure.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PreflightError'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '402':
          $ref: '#/components/responses/BillingRequired'
        '409':
          description: That hostname is already connected.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          $ref: '#/components/responses/RateLimited'
  /v1/domains/{id}:
    parameters:
    - $ref: '#/components/parameters/DomainId'
    get:
      operationId: getDomain
      summary: Retrieve one domain
      tags:
      - Domains
      responses:
        '200':
          $ref: '#/components/responses/DomainEnvelope'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
    patch:
      operationId: updateDomain
      summary: Update a domain's origin or routing options
      description: Only the fields you send change. The hostname itself is immutable.
      tags:
      - Domains
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                originUrl:
                  type: string
                  format: uri
                metadata:
                  type: object
                  additionalProperties: true
                mode:
                  type: string
                  enum:
                  - proxy
                  - redirect
                keepHost:
                  type: boolean
                redirectWww:
                  type: boolean
                redirectStatus:
                  type: integer
                  enum:
                  - 301
                  - 302
              minProperties: 1
      responses:
        '200':
          $ref: '#/components/responses/DomainEnvelope'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
    delete:
      operationId: deleteDomain
      summary: Disconnect a domain
      description: Removes the edge configuration and the certificate. Not reversible.
      tags:
      - Domains
      responses:
        '200':
          $ref: '#/components/responses/Deleted'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
  /v1/domains/{id}/check:
    parameters:
    - $ref: '#/components/parameters/DomainId'
    post:
      operationId: checkDomain
      summary: Re-run DNS and SSL checks now
      description: Forces the monitor to refresh instead of waiting for its next pass. Use it right after telling a customer to add their records.
      tags:
      - Domains
      responses:
        '200':
          $ref: '#/components/responses/DomainEnvelope'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
  /v1/domains/{id}/instructions:
    parameters:
    - $ref: '#/components/parameters/DomainId'
    get:
      operationId: getConnectInstructions
      parameters:
      - name: redirect_uri
        in: query
        required: false
        schema:
          type: string
        description: Where the customer lands after approving a Domain Connect flow at their provider, with state appended to its query string. Cloudflare accepts any URL; other providers may require the domainee.dev redirect domain declared in the templates, so when opening applyUrl in a popup you can omit it and call POST /v1/domains/{id}/check when the customer returns.
      - name: state
        in: query
        required: false
        schema:
          type: string
        description: Opaque value round-tripped through the Domain Connect redirect.
      summary: DNS steps for the customer's actual DNS provider
      description: 'Fingerprints the DNS provider from the zone''s public NS records and returns steps written for it: the right record type for this position and provider, the record name written the way that provider''s form wants it (@ vs blank vs the full domain), where the record editor lives in its UI, and the provider-specific gotchas that silently break a connection. Also reports whether the provider supports one-click Domain Connect, in which case domainConnect.applyUrl sends the customer to approve the change at their own provider. Does live NS and Domain Connect lookups, so it is a separate endpoint rather than a field on the domain resource. Never fails: an unrecognised provider returns provider.detected=false with correct generic steps.'
      tags:
      - Domains
      responses:
        '200':
          description: Provider-aware connect instructions.
          content:
            application/json:
              schema:
                type: object
                properties:
                  instructions:
                    type: object
                    properties:
                      hostname:
                        type: string
                      provider:
                        type: object
                        properties:
                          id:
                            type: string
                            example: cloudflare
                          name:
                            type: string
                            example: Cloudflare
                          detected:
                            type: boolean
                      apex:
                        type: boolean
                      method:
                        type: string
                        enum:
                        - a
                        - cname
                      methodReason:
                        type: string
                      records:
                        type: array
                        items:
                          type: object
                          properties:
                            type:
                              type: string
                              enum:
                              - A
                              - CNAME
                            name:
                              type: string
                            value:
                              type: string
                            ttl:
                              type: integer
                      steps:
                        type: array
                        items:
                          type: string
                      quirks:
                        type: array
                        items:
                          type: string
                      dashboardUrl:
                        type: string
                      domainConnect:
                        type: object
                        description: One-click setup via Domain Connect when the customer's provider supports it and has adopted Domainee's template. supported=false carries a reason instead; always render steps regardless, since coverage is far from universal.
                        properties:
                          supported:
                            type: boolean
                          reason:
                            type: string
                            enum:
                            - no_discovery_record
                            - settings_unavailable
                            - no_sync_support
                            - template_not_adopted
                            - lookup_failed
                          providerId:
                            type: string
                          providerName:
                            type: string
                          providerDisplayName:
                            type: string
                            description: The provider's own display name when it publishes one. Prefer it over providerName in UI.
                          applyUrl:
                            type: string
                            description: Send the customer here to approve the change at their own DNS provider.
                          width:
                            type: integer
                          height:
                            type: integer
                          urlControlPanel:
                            type: string
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
  /v1/domains/{id}/connect-session:
    parameters:
    - $ref: '#/components/parameters/DomainId'
    post:
      operationId: createConnectSession
      summary: Mint a browser-safe token for the embeddable widget
      description: 'Returns a short-lived token scoped to this one domain, safe to hand to a browser. Call it from your server with your API key, then pass the token to Domainee.mount(). The token can only read the domain, read its connect instructions, force a DNS check, and apply records with a customer-supplied DNS token; everything else returns 403, including other domains and minting another session. Tokens are signed rather than stored, so they cannot be revoked before expiry: keep ttlSeconds short. Default 3600, maximum 86400. Never send an sk_live_ key to a browser.'
      tags:
      - Domains
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
              properties:
                ttlSeconds:
                  type: integer
                  minimum: 60
                  maximum: 86400
                  default: 3600
      responses:
        '200':
          description: A scoped, short-lived widget token.
          content:
            application/json:
              schema:
                type: object
                properties:
                  connectSession:
                    type: object
                    properties:
                      token:
                        type: string
                      expiresAt:
                        type: string
                        format: date-time
                      domainId:
                        type: string
                      hostname:
                        type: string
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
  /v1/domains/{id}/dns/apply:
    parameters:
    - $ref: '#/components/parameters/DomainId'
    post:
      operationId: applyDnsWithToken
      summary: Point a domain using the customer's own scoped DNS token
      description: 'Writes the records at the customer''s DNS provider using a scoped API token they supply. The token is used for the request and discarded: never stored, never logged, never returned. Send it in the body, not the query string, so it stays out of access logs. Touches only conflicting A/AAAA/CNAME/ALIAS records at the hostname being pointed, leaving MX, TXT and every other name intact. On Cloudflare, records are created with proxy disabled, since an orange-clouded record prevents certificate issuance. Supported providers: cloudflare, digitalocean.'
      tags:
      - Domains
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                provider:
                  type: string
                  enum:
                  - cloudflare
                  - digitalocean
                token:
                  type: string
                  description: The customer's scoped DNS API token. Cloudflare needs Zone:Zone:Read and Zone:DNS:Edit; DigitalOcean needs write scope on Domains.
              required:
              - provider
              - token
      responses:
        '200':
          description: Records written and the domain re-probed.
          content:
            application/json:
              schema:
                type: object
                properties:
                  applied:
                    type: object
                    properties:
                      provider:
                        type: string
                      written:
                        type: array
                        items:
                          type: object
                      removed:
                        type: integer
                        description: Conflicting records deleted to make room.
                  domain:
                    $ref: '#/components/schemas/Domain'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '422':
          description: The provider refused the write, or we can't write to it.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
components:
  schemas:
    Domain:
      type: object
      description: A customer hostname connected to one of your origins.
      properties:
        id:
          type: string
          description: Domainee identifier.
          example: dom_9f2c
        workspaceId:
          type: string
          description: Workspace the domain belongs to.
        hostname:
          type: string
          example: app.acme.com
        originUrl:
          type: string
          format: uri
          description: Where verified traffic is sent.
          example: https://acme.vercel.app
        metadata:
          type: object
          additionalProperties: true
          description: Opaque key/value bag echoed back on reads and webhooks.
        mode:
          type: string
          enum:
          - proxy
          - redirect
          description: '`proxy` serves the origin under the custom hostname; `redirect` sends a 301/302.'
        keepHost:
          type: boolean
          description: Forward the original Host header to the origin instead of rewriting it.
        redirectWww:
          type: boolean
          description: Also answer for the www. variant.
        redirectStatus:
          type: integer
          enum:
          - 301
          - 302
        status:
          type: string
          enum:
          - pending
          - verified
          - failed
          - expired
          description: Verification state. Traffic only flows once `verified`.
        dnsRecords:
          type: array
          description: The records the domain owner must create for verification to pass. Always the single CNAME. Unchanged and safe to keep rendering as-is; read `dnsRecordOptions` if you want to offer the apex A-record path.
          items:
            $ref: '#/components/schemas/DnsRecordInstruction'
        dnsRecordOptions:
          type: array
          description: 'Every supported way to point this hostname at the edge, recommended first — take `[0]` to get the right one. For a subdomain that is the `cname` set. For an apex it is the `a` set: a root domain cannot hold a CNAME, and the ALIAS/ANAME/flattening substitutes all pin the name to a single region with no failover, whereas the anycast A records keep every region.'
          items:
            $ref: '#/components/schemas/DnsRecordSet'
        verificationToken:
          type: string
        verifiedAt:
          type:
          - string
          - 'null'
          format: date-time
        lastCheckedAt:
          type:
          - string
          - 'null'
          format: date-time
        isResolving:
          type: boolean
          description: The hostname resolves in public DNS.
        pointsToEdge:
          type: boolean
          description: It resolves to Domainee's edge.
        dnsPointedAt:
          type: array
          items:
            type: string
          description: What the hostname currently resolves to.
        sslActiveFrom:
          type:
          - string
          - 'null'
          format: date-time
        sslActiveUntil:
          type:
          - string
          - 'null'
          format: date-time
        monitorStatus:
          type: string
          enum:
          - unknown
          - dns_not_resolving
          - dns_incorrect
          - pending_ssl
          - active_ssl
          - target_not_loading
          - ssl_failed
          - ssl_expired
          description: Live health, refreshed by the DNS monitor and by POST /v1/domains/{id}/check.
        monitorMessage:
          type:
          - string
          - 'null'
        lastMonitoredAt:
          type:
          - string
          - 'null'
          format: date-time
        createdAt:
          type: string
          format: date-time
        updatedAt:
          type: string
          format: date-time
      required:
      - id
      - workspaceId
      - hostname
      - originUrl
      - status
      - createdAt
    PreflightError:
      type: object
      description: A rejected domain creation. `warnings` lists the specific DNS or CAA problems found.
      properties:
        error:
          type: string
          example: preflight_failed
        message:
          type: string
        warnings:
          type: array
          items:
            $ref: '#/components/schemas/PreflightWarning'
      required:
      - error
      - message
    DnsRecordInstruction:
      type: object
      description: A record the domain owner must create for verification to pass.
      properties:
        name:
          type: string
          example: app.acme.com
        value:
          type: string
          example: edge.domainee.dev
        type:
          type: string
          example: CNAME
        purpose:
          type: string
          example: Traffic Routing
      required:
      - name
      - value
      - type
    Error:
      type: object
      description: The error envelope used by every authenticated endpoint.
      properties:
        error:
          type: string
          description: Stable machine-readable code.
          example: not_found
        message:
          type: string
          description: Human-readable explanation.
      required:
      - error
      - message
    PreflightWarning:
      type: object
      properties:
        code:
          type: string
          enum:
          - caa_blocks_lets_encrypt
          - dns_unresolvable
          - origin_unreachable
          - origin_ssrf_blocked
        message:
          type: string
        caaRecords:
          type: array
          items:
            type: string
          description: Present on `caa_blocks_lets_encrypt`.
        details:
          type: string
          description: May be present on `origin_unreachable`.
        resolvedIps:
          type: array
          items:
            type: string
          description: Present on `origin_ssrf_blocked`.
      required:
      - code
      - message
    DnsRecordSet:
      type: object
      description: One way of pointing the hostname at the edge. Every record in `records` must be created for the set to work.
      properties:
        method:
          type: string
          enum:
          - cname
          - a
          description: '`cname` works wherever the provider allows a CNAME at this position. `a` is for apex/root domains on providers with no ALIAS or ANAME support.'
        label:
          type: string
          example: A records
        recommended:
          type: boolean
        note:
          type: string
        records:
          type: array
          items:
            $ref: '#/components/schemas/DnsRecordInstruction'
      required:
      - method
      - label
      - recommended
      - records
  parameters:
    DomainId:
      name: id
      in: path
      required: true
      description: Domain identifier returned by `createDomain`.
      schema:
        type: string
    IdempotencyKey:
      name: Idempotency-Key
      in: header
      required: false
      description: Any unique string. Retrying with the same key replays the first response instead of repeating the work.
      schema:
        type: string
  responses:
    NotFound:
      description: No such object in this workspace.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    RateLimited:
      description: More than 60 requests in a minute for this key.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    DomainEnvelope:
      description: The domain.
      content:
        application/json:
          schema:
            type: object
            properties:
              domain:
                $ref: '#/components/schemas/Domain'
            required:
            - domain
    Unauthorized:
      description: Missing, malformed, revoked, or expired Bearer token.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    Deleted:
      description: Deleted.
      content:
        application/json:
          schema:
            type: object
            properties:
              deleted:
                type: boolean
            required:
            - deleted
    BillingRequired:
      description: The workspace has no active subscription or the charge failed.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    BadRequest:
      description: The request body or query failed validation.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: An API key (`sk_live_…`) from https://domainee.dev/developers or an OAuth access token (`dmn_oat_…`). Both are workspace-scoped.
externalDocs:
  description: Domainee documentation
  url: https://domainee.dev/docs