> ## Documentation Index
> Fetch the complete documentation index at: https://docs.fingerprint.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Register a subdomain

> Register a new custom subdomain and receive its DNS records. Optionally pass `webhook_url` to get a signed POST callback on every status change instead of polling; the signing secret is returned once in this response as `webhook_secret`.

Every DNS record you need is returned in this response at once: the verification `CNAME` record and the two routing `A` records. You can add them all to your DNS provider in a single pass. The `A` records start routing traffic only after your certificate is issued, so adding them early is safe.

Optionally pass a `webhook_url` to receive a signed `POST` callback each time the subdomain's status changes, instead of polling [Get subdomain](/reference/subdomainscontroller_findone). The `webhook_secret` used to verify those callbacks is returned only in this response, so store it now. It can't be retrieved later.

Subdomains are immutable. To change a subdomain, [delete it](/reference/subdomainscontroller_delete) and register a new one.

See the [Custom subdomain setup](/docs/custom-subdomain-setup#manage-subdomains-with-the-management-api) guide for the full flow.


## OpenAPI

````yaml reference/management-api_2025-11-20.json POST /subdomains
openapi: 3.0.0
info:
  title: Management API
  description: >-
    Managment API allows you to manage your Fingerprint account and applications
    programmatically from a server environment.
  version: '2025-11-20'
  contact: {}
servers:
  - url: https://management-api.fpjs.io
security:
  - Management-API-key: []
tags: []
paths:
  /subdomains:
    post:
      tags:
        - subdomains
      summary: Create subdomain
      description: >-
        Register a new custom subdomain and receive its DNS records. Optionally
        pass `webhook_url` to get a signed POST callback on every status change
        instead of polling; the signing secret is returned once in this response
        as `webhook_secret`.
      operationId: SubdomainsController_create
      parameters:
        - name: X-API-Version
          in: header
          description: Management API version.
          example: '2025-11-20'
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateSubdomainDto'
      responses:
        '201':
          description: Subdomain created.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/2c18b7e424f401c718c91'
        '401':
          description: 'Error: Invalid API key.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiExceptionDto'
        '409':
          description: >-
            Subdomain already exists in this workspace, or workspace subdomain
            limit reached.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiExceptionDto'
        '422':
          description: Invalid subdomain or request body.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ValidationApiExceptionDto'
        '429':
          description: 'Error: API key has exceeded its rate limit.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiExceptionDto'
components:
  schemas:
    CreateSubdomainDto:
      type: object
      properties:
        subdomain:
          type: string
          description: The FQDN to register as a custom subdomain.
          example: metrics.yourwebsite.com
          maxLength: 64
        webhook_url:
          type: string
          description: >-
            HTTPS URL that receives a signed POST callback each time the
            subdomain status changes, removing the need to poll `GET
            /subdomains/{id}`. The URL must be publicly reachable — IP
            addresses, custom ports and hosts resolving to private networks are
            rejected. Deliveries are signed; see `webhook_secret` in the create
            response for the verification scheme.
          example: https://customer.com/hooks/fp-subdomain
          maxLength: 2048
      required:
        - subdomain
    2c18b7e424f401c718c91:
      type: object
      properties:
        data:
          $ref: '#/components/schemas/CreateSubdomainResponseDto'
      required:
        - data
    ApiExceptionDto:
      type: object
      properties:
        error:
          $ref: '#/components/schemas/BaseApiExceptionDto'
      required:
        - error
    ValidationApiExceptionDto:
      type: object
      properties:
        error:
          $ref: '#/components/schemas/BaseValidationApiExceptionDto'
      required:
        - error
    CreateSubdomainResponseDto:
      type: object
      properties:
        id:
          type: string
          description: Subdomain ID
          example: certv2_a1b2c3d4e5f6
        subdomain:
          type: string
          description: Subdomain hostname
          example: metrics.yourwebsite.com
        status:
          type: string
          enum:
            - pending
            - active
            - timed_out
            - failed
          description: >-
            Current subdomain status. `pending` = DNS/certificate setup in
            progress; `active` = live and serving traffic; `timed_out` = setup
            timed out (usually the required DNS records were never added) — safe
            to delete and recreate; `failed` = terminal failure.
        created_at:
          format: date-time
          type: string
          description: When the subdomain was created
          example: '2026-03-05T14:00:00.000Z'
        updated_at:
          format: date-time
          type: string
          description: When the subdomain was last updated
          example: '2026-03-05T14:35:00.000Z'
        webhook_url:
          type: string
          nullable: true
          description: >-
            HTTPS URL receiving a signed POST callback on every status change.
            Null when not configured. Set at creation only — subdomains are
            immutable.
          example: https://customer.com/hooks/fp-subdomain
        dns_records:
          $ref: '#/components/schemas/DnsRecordsDto'
        webhook_secret:
          type: string
          nullable: true
          description: >-
            Secret for verifying status webhook signatures. **Only returned on
            creation — store it now; it cannot be retrieved later.** Null when
            no `webhook_url` was provided.


            Every webhook request carries an `X-Fingerprint-Signature: t=<unix
            timestamp>,v1=<signature>` header, where `<signature>` is the
            hex-encoded HMAC-SHA256 of `{t}.{raw request body}` keyed with this
            secret. To verify: read the raw (unparsed) request body, recompute
            the HMAC, and compare it to `v1` using a constant-time comparison.
            Ignore schemes other than `v1`. Reject stale timestamps to prevent
            replays (recommended tolerance: 5 minutes).


            Failed deliveries are retried 3 times (after 1s, 10s and 60s), then
            dropped — webhook failures never block status transitions; `GET
            /subdomains/{id}` always reflects the current state.
          example: whsec_wRNftLajMZNeslQOP6vEPm4iVx5NlZ6z
      required:
        - id
        - subdomain
        - status
        - created_at
        - updated_at
        - dns_records
    BaseApiExceptionDto:
      type: object
      properties:
        message:
          type: string
          description: Verbal description of the error.
        code:
          type: string
          description: Error code.
      required:
        - message
        - code
    BaseValidationApiExceptionDto:
      type: object
      properties:
        message:
          type: string
          description: Verbal description of the error.
        code:
          type: string
          description: Error code.
        violations:
          description: List of validation violations.
          type: array
          items:
            $ref: '#/components/schemas/BaseValidationApiExceptionViolationDto'
      required:
        - message
        - code
    DnsRecordsDto:
      type: object
      properties:
        verification:
          $ref: '#/components/schemas/DnsRecordDto'
        routing:
          type: array
          items:
            $ref: '#/components/schemas/DnsRecordDto'
      required:
        - verification
        - routing
    BaseValidationApiExceptionViolationDto:
      type: object
      properties:
        property:
          type: string
          description: Property that has failed validation.
        message:
          type: string
          description: Description of the violation.
      required:
        - property
        - message
    DnsRecordDto:
      type: object
      properties:
        type:
          type: string
          description: DNS record type the customer must create.
          example: CNAME
        host:
          type: string
          description: Host/name of the DNS record.
          example: _acme-challenge.metrics.yourwebsite.com
        value:
          type: string
          description: Value the DNS record must point to.
          example: dcv.fpjs.io
        status:
          type: string
          enum:
            - pending_validation
            - validated
            - failed
          description: Whether this individual record has been detected and validated yet.
          example: pending_validation
      required:
        - type
        - host
        - value
        - status
  securitySchemes:
    Management-API-key:
      scheme: bearer
      bearerFormat: JWT
      type: http

````