---
title: "Register a storefront domain for wallet buttons"
description: "Apple Pay will not render its button on a domain the gateway has not registered, and it fails SILENTLY — no error, no console warning, no failed request — so an unregistered domain is…"
canonical_url: "https://twominutebilling.com/help/developers/api-reference/payment-methods/register-a-storefront-domain-for-wallet-buttons"
---
# Register a storefront domain for wallet buttons

> Apple Pay will not render its button on a domain the gateway has not registered, and it fails SILENTLY — no error, no console warning, no failed request — so an unregistered domain is…

<api-operation method="POST" path="/v1/businesses/{biz}/gateway_accounts/{gwa}/wallet_domains">
<template v-slot:docs="">

Apple Pay will not render its button on a domain the gateway has not registered, and it fails SILENTLY — no error, no console warning, no failed request — so an unregistered domain is indistinguishable from no support at all. Nothing else is needed for wallets: an Apple Pay token is a card credential, Stripe's PaymentElement surfaces the button on its own, and a saved instrument reads back as `Apple Pay` rather than a bare card brand. Registering re-validates every time, because a domain registered before its association file was in place stays inactive until something asks again. The per-wallet `active` flags and any `detail` are the GATEWAY's verdict, not ours — `detail` usually names the real cause, which is that `/.well-known/apple-developer-merchantid-domain-association` is not reachable on that domain. Safe to repeat: a domain the gateway already knows reports its status instead of erroring. Gateways with no such concept answer 409.

## Path parameters

<table>
<thead>
  <tr>
    <th>
      Parameter
    </th>
    
    <th>
      Type
    </th>
    
    <th>
      Required
    </th>
    
    <th>
      Description
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        biz
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <strong>
        yes
      </strong>
    </td>
    
    <td>
      Business id (biz_…)
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        gwa
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      <strong>
        yes
      </strong>
    </td>
    
    <td>
      The gateway account's id (gwa_…).
    </td>
  </tr>
</tbody>
</table>

## Headers

<table>
<thead>
  <tr>
    <th>
      Parameter
    </th>
    
    <th>
      Type
    </th>
    
    <th>
      Required
    </th>
    
    <th>
      Description
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        Idempotency-Key
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      no
    </td>
    
    <td>
      Safely retry writes — repeats return the original response. If a write FAILED after it had already taken effect, that failure is what repeats: the key is spent, and a new attempt needs a new key.
    </td>
  </tr>
</tbody>
</table>

## Body

The request body is optional.

<table>
<thead>
  <tr>
    <th>
      Field
    </th>
    
    <th>
      Type
    </th>
    
    <th>
      Required
    </th>
    
    <th>
      Description
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        domain
      </code>
    </td>
    
    <td>
      <code>
        string
      </code>
    </td>
    
    <td>
      no
    </td>
    
    <td>
      A bare hostname such as <code>
        billing.example.com
      </code>
      
       — no scheme, port or path. OMIT IT in the normal case: the domain that matters is the STOREFRONT's, which this deployment already names in <code>
        PORTAL_BASE_URL
      </code>
      
      , and in production that is a different host from the dashboard (<code>
        portal.…
      </code>
      
       vs <code>
        app.…
      </code>
      
      ). Registering the dashboard's domain succeeds and leaves the wallet dark, which is the hardest failure of all to diagnose. Pass one only to override. 400 if it is omitted and no <code>
        PORTAL_BASE_URL
      </code>
      
       is set.
    </td>
  </tr>
</tbody>
</table>

## Status codes

<table>
<thead>
  <tr>
    <th>
      Status
    </th>
    
    <th>
      Meaning
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        200
      </code>
    </td>
    
    <td>
      The domain's wallet status at the gateway
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        400
      </code>
    </td>
    
    <td>
      Not a bare hostname, or no domain given and no PORTAL_BASE_URL configured
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        404
      </code>
    </td>
    
    <td>
      No such gateway account
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        409
      </code>
    </td>
    
    <td>
      This gateway does not register wallet domains
    </td>
  </tr>
</tbody>
</table>
</template>

<template v-slot:request="">

```bash [Request]
curl -X POST "$TMBILLING_URL/v1/businesses/biz_XXXXXXXXXXXX01/gateway_accounts/gwa_XXXXXXXXXXXX01/wallet_domains" \
  -H "Authorization: Bearer $TMBILLING_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "domain": "example.com"
  }'
```

</template>

<template v-slot:response="">
<code-group>

```json [200 OK]
{}
```

```json [400 Bad Request]
{
  "error": {
    "type": "invalid_request_error",
    "message": "Not a bare hostname, or no domain given and no PORTAL_BASE_URL configured"
  }
}
```

```json [404 Not Found]
{
  "error": {
    "type": "invalid_request_error",
    "message": "No such gateway account"
  }
}
```

```json [409 Conflict]
{
  "error": {
    "type": "conflict_error",
    "message": "This gateway does not register wallet domains"
  }
}
```

</code-group>
</template>
</api-operation>


## Sitemap

See the full [sitemap](https://twominutebilling.com/sitemap.md) for all pages.
