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

# Add custom domain

> <Info>This API is in beta. Endpoints, fields, and behavior may still change, so avoid depending on it in production.</Info>

Attaches a domain you own to the app. Nothing serves on it until you link it with [Link custom domain](/api-reference/link-custom-domain).

Adding a domain and linking it are separate steps. DNS propagation happens outside Base44, so a linked domain can sit unverified for as long as its nameservers take. The domain's DNS has to point at Base44 before verification can pass, and the Base44 editor's domain settings show the records to set.

If a domain was released by another workspace, the call is rejected with a TXT record to publish. Publish it at the domain's DNS provider and send the same request again to prove you control the domain. A domain your own workspace released is re-added straight away.

Some Enterprise workspaces serve all their apps under one app domain, such as `apps.example.com`. A host at or under another workspace's app domain is rejected outright. That workspace has already proved it controls the zone, so no TXT record is offered. Hosts under your own workspace's app domain are rejected too while it's connected, because it gives each app its address there.

Don't retry a request that timed out without checking first. If the domain was added, a second request is rejected as already attached, so look for it in [List custom domains](/api-reference/list-custom-domains) before you send the request again.

Adding domains is limited to 30 requests an hour per caller. Some workspaces have a different limit.

<Warning>The response includes fields beyond the ones documented here. Don't rely on undocumented response fields, as they can change at any time.</Warning>



## OpenAPI

````yaml /developers/references/app-management/app-management-openapi.json post /api/apps/{app_id}/custom-domains
openapi: 3.1.0
info:
  title: Base44 App Management API
  version: 1.0.0
servers:
  - url: https://app.base44.com
security:
  - PersonalAccessTokenAuth: []
paths:
  /api/apps/{app_id}/custom-domains:
    post:
      summary: Add custom domain
      description: >-
        <Info>This API is in beta. Endpoints, fields, and behavior may still
        change, so avoid depending on it in production.</Info>


        Attaches a domain you own to the app. Nothing serves on it until you
        link it with [Link custom domain](/api-reference/link-custom-domain).


        Adding a domain and linking it are separate steps. DNS propagation
        happens outside Base44, so a linked domain can sit unverified for as
        long as its nameservers take. The domain's DNS has to point at Base44
        before verification can pass, and the Base44 editor's domain settings
        show the records to set.


        If a domain was released by another workspace, the call is rejected with
        a TXT record to publish. Publish it at the domain's DNS provider and
        send the same request again to prove you control the domain. A domain
        your own workspace released is re-added straight away.


        Some Enterprise workspaces serve all their apps under one app domain,
        such as `apps.example.com`. A host at or under another workspace's app
        domain is rejected outright. That workspace has already proved it
        controls the zone, so no TXT record is offered. Hosts under your own
        workspace's app domain are rejected too while it's connected, because it
        gives each app its address there.


        Don't retry a request that timed out without checking first. If the
        domain was added, a second request is rejected as already attached, so
        look for it in [List custom domains](/api-reference/list-custom-domains)
        before you send the request again.


        Adding domains is limited to 30 requests an hour per caller. Some
        workspaces have a different limit.


        <Warning>The response includes fields beyond the ones documented here.
        Don't rely on undocumented response fields, as they can change at any
        time.</Warning>
      operationId: create_domain_api_apps__app_id__custom_domains_post
      parameters:
        - name: app_id
          in: path
          required: true
          schema:
            type: string
            description: ID of the app the domain belongs to.
            title: App Id
          description: ID of the app the domain belongs to.
          example: 6820f3a4e7b91d003c45a1f2
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateDomainBody'
      responses:
        '200':
          description: The domain, attached to the app but not yet linked.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CustomDomainResource'
        '400':
          description: >-
            The domain is already attached to an app, contains a social media
            platform's name, or you have reached the limit of 350 custom
            domains. Base44's hosting provider can also reject it. A host at or
            under a workspace's app domain comes back in the standard error
            envelope, with `error.code` set to `under_workspace_domain`.
        '401':
          description: Missing or invalid credentials.
        '402':
          description: This workspace's plan doesn't include custom domains.
        '403':
          description: >-
            You don't have access to this app, or you used a workspace API key.
            These endpoints take a personal API key.
        '404':
          description: App not found.
        '409':
          description: >-
            Another workspace released this domain: publish the TXT record in
            `detail.verification` and retry. Or the domain is connected to
            another hosting account (`hostname_claimed_elsewhere`, in the
            standard error envelope): its owner must remove it there first.
          content:
            application/json:
              schema:
                anyOf:
                  - $ref: '#/components/schemas/DomainClaimChallenge'
                  - $ref: '#/components/schemas/StandardErrorEnvelope'
                title: >-
                  Response 409 Create Domain Api Apps  App Id  Custom Domains
                  Post
        '422':
          description: '`domain` is missing or empty.'
        '429':
          description: >-
            Rate limit reached, either yours or Base44's hosting provider's.
            Retry later.
      security:
        - PersonalAccessTokenAuth:
            - full_access
components:
  schemas:
    CreateDomainBody:
      properties:
        domain:
          type: string
          title: Domain
          description: >-
            The domain to add. A scheme, a trailing slash, and a leading `www.`
            are removed, and it's stored in lower case.
          example: example.com
      type: object
      required:
        - domain
      title: CreateDomainBody
    CustomDomainResource:
      properties:
        id:
          type: string
          title: Id
          description: >-
            ID of the domain. Pass it as `domain_id` to the other domain
            endpoints.
          example: 68b1c0d4e7b91d003c45a1f7
        domain:
          type: string
          title: Domain
          description: >-
            The domain name, normalized to lower case with any scheme, trailing
            slash and leading `www.` removed.
          example: example.com
        app_id:
          type: string
          title: App Id
          description: ID of the app the domain serves.
          example: 6820f3a4e7b91d003c45a1f2
        disabled:
          type: boolean
          title: Disabled
          description: >-
            Whether Base44 has stopped serving the domain. A disabled domain
            stays attached to the app and keeps its DNS, and serves a
            placeholder page instead of the app.
          example: false
        last_status_check:
          anyOf:
            - type: string
              format: date-time
            - type: 'null'
          title: Last Status Check
          description: >-
            When [Get custom domain
            status](/api-reference/get-custom-domain-status) last ran for this
            domain, as a UTC timestamp in ISO 8601 format, or `null` if it never
            has.
          example: '2026-08-15T09:10:00'
        last_status_payload:
          anyOf:
            - $ref: '#/components/schemas/CustomDomainStatusPayload'
            - type: 'null'
          description: >-
            The provider's last report on the domain, or `null` until a status
            check has run.
        redirect_target_domain:
          anyOf:
            - type: string
            - type: 'null'
          title: Redirect Target Domain
          description: >-
            The domain that visitors to this one are sent to with a 301
            redirect. The value is `null` when this domain serves the app
            itself.
          example: www.example.com
        provider_id:
          anyOf:
            - type: string
            - type: 'null'
          title: Provider Id
          description: >-
            Names the registrar when the domain was bought through Base44's Wix
            flow. The value is `null` otherwise, which covers both a domain you
            own elsewhere and one bought through the Entri flow, so this isn't a
            purchased-versus-external signal.
          example: wix
      type: object
      required:
        - id
        - domain
        - app_id
        - disabled
      title: CustomDomainResource
      description: A custom domain attached to an app.
    DomainClaimChallenge:
      properties:
        detail:
          $ref: '#/components/schemas/DomainClaimChallengeDetail'
          description: What to publish before adding the domain again.
      type: object
      required:
        - detail
      title: DomainClaimChallenge
      description: >-
        Another workspace released this domain, so you have to prove you control
        it.
    StandardErrorEnvelope:
      properties:
        error:
          $ref: '#/components/schemas/StandardError'
          description: What went wrong.
      type: object
      required:
        - error
      title: StandardErrorEnvelope
      description: The error body returned in the standard envelope.
    CustomDomainStatusPayload:
      properties:
        verificationStatus:
          anyOf:
            - type: string
            - type: 'null'
          title: Verificationstatus
          description: >-
            Whether the provider has verified the domain's DNS. Only `verified`
            means the domain serves the app, and anything else means it doesn't
            yet. Absent until [Get custom domain
            status](/api-reference/get-custom-domain-status) has run once.
          example: verified
      type: object
      title: CustomDomainStatusPayload
      description: What the hosting provider last reported about the domain.
    DomainClaimChallengeDetail:
      properties:
        message:
          type: string
          title: Message
          description: Why the domain can't be added yet.
          example: >-
            This domain is registered to another workspace. Add the TXT record
            below to prove you control it, then try again.
        verification:
          $ref: '#/components/schemas/DomainClaimVerification'
          description: The record to publish at the domain's DNS provider before you retry.
      type: object
      required:
        - message
        - verification
      title: DomainClaimChallengeDetail
    StandardError:
      properties:
        code:
          type: string
          title: Code
          description: >-
            Stable machine-readable cause of the failure. Match on this, not on
            `message`.
          example: not_found
        message:
          type: string
          title: Message
          description: Human-readable explanation. It isn't a stable contract.
          example: App not found
        details:
          additionalProperties: true
          type: object
          title: Details
          description: >-
            Structured context for the failure, or an empty object when there's
            none.
          example: {}
      type: object
      required:
        - code
        - message
      title: StandardError
    DomainClaimVerification:
      properties:
        type:
          type: string
          title: Type
          description: Record type. Always `TXT`.
          example: TXT
        name:
          type: string
          title: Name
          description: Host name to publish the record on.
          example: _b44-verify.example.com
        value:
          type: string
          title: Value
          description: >-
            Value to publish. It's issued to your workspace, so another
            workspace can't use it to claim the domain.
          example: base44-domain-claim=3kQ9vL2mP7xR8tN4wZ1yB6cF5hJ0dS2aE9gU7iO3rT
      type: object
      required:
        - type
        - name
        - value
      title: DomainClaimVerification
      description: The DNS record that proves you control the domain.
  securitySchemes:
    PersonalAccessTokenAuth:
      type: http
      scheme: bearer
      description: >-
        Personal access token, sent as `Authorization: Bearer <token>`. Each
        operation lists the permission it needs: `read_only` or `full_access`. A
        full-access token can call every operation. A read-only token can call
        only the operations that list `read_only`; anything else returns 403
        `read_only_credential_route_not_allowed`.

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.