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

# Embed address collection

> Add Numeral's secure address collector to your checkout with the numeral-checkout custom element.

Embedded collection keeps the address validation experience inside your checkout. Your server creates an embedded Bridge session with the `numeral-tax` SDK, and the browser mounts Numeral's `<numeral-checkout>` custom element using the returned session capability.

<Info>
  The collector appears inside your page, but its sensitive form runs in a sandboxed, cross-origin iframe served by Numeral. Your page receives state, requirement codes, and tax amounts—not the buyer's address or tax IDs.
</Info>

## 1. Allow your checkout origin

In **Developers → Numeral for Stripe Checkout**, add the exact origin of your checkout page to **Allowed embed origins**. For example:

```text theme={null}
https://store.example
```

Success and cancel URLs must also use an origin listed under **Allowed redirect origins**. Click **Save and publish** after changing either list.

## 2. Create an embedded session on your server

<CodeGroup>
  ```ts Node.js theme={null}
  import NumeralAPI from "numeral-tax";

  const numeral = new NumeralAPI({
    apiKey: process.env.NUMERAL_API_KEY!,
  });

  export async function POST() {
    const session = await numeral.tax.bridge.sessions.create({
      config_id: process.env.NUMERAL_STRIPE_CHECKOUT_CONFIG_ID!,
      collection_mode: "embedded",
      confirmation_method: "automatic",
      checkout: {
        mode: "payment",
        line_items: [{ price: "price_123", quantity: 1 }],
        success_url: "https://store.example/success",
        cancel_url: "https://store.example/cart",
      },
      tax_context: {
        location: {
          basis: "billing_address",
          address: { country: "US" },
        },
      },
      "X-API-Version": "2026-03-01",
      "Idempotency-Key": crypto.randomUUID(),
    });

    return Response.json({
      sessionId: session.id,
      clientSecret: session.client_secret,
    });
  }
  ```

  ```go Go theme={null}
  client := numeraltax.NewClient(
      option.WithAPIKey(os.Getenv("NUMERAL_API_KEY")),
  )

  session, err := client.Tax.Bridge.Sessions.New(ctx, numeraltax.TaxBridgeSessionNewParams{
      XAPIVersion:        numeraltax.TaxBridgeSessionNewParamsXAPIVersion2026_03_01,
      IdempotencyKey:     numeraltax.String("checkout_request_8421"),
      ConfigID:           os.Getenv("NUMERAL_STRIPE_CHECKOUT_CONFIG_ID"),
      CollectionMode:     numeraltax.TaxBridgeSessionNewParamsCollectionModeEmbedded,
      ConfirmationMethod: numeraltax.TaxBridgeSessionNewParamsConfirmationMethodAutomatic,
      Checkout: numeraltax.TaxBridgeSessionNewParamsCheckout{
          Mode: "payment",
          LineItems: []numeraltax.TaxBridgeSessionNewParamsCheckoutLineItem{{
              Price: "price_123", Quantity: 1,
          }},
          SuccessURL: numeraltax.String("https://store.example/success"),
          CancelURL:  numeraltax.String("https://store.example/cart"),
      },
      TaxContext: numeraltax.TaxBridgeSessionNewParamsTaxContext{
          Location: numeraltax.TaxBridgeSessionNewParamsTaxContextLocationUnion{
              OfTaxBridgeSessionNewsTaxContextLocationObject: &numeraltax.TaxBridgeSessionNewParamsTaxContextLocationObject{
                  Basis: "billing_address",
                  Address: numeraltax.TaxBridgeSessionNewParamsTaxContextLocationObjectAddress{
                      Country: "US",
                  },
              },
          },
      },
  })
  if err != nil {
      return err
  }

  return json.NewEncoder(response).Encode(map[string]string{
      "sessionId":    session.ID,
      "clientSecret": session.ClientSecret,
  })
  ```
</CodeGroup>

<Warning>
  The Numeral API key stays on your server. Only the session ID and session-scoped `client_secret` are sent to the browser.
</Warning>

You can also set [discount options](/integrations/stripe/stripe-checkout-discounts) and [metadata and saved-card options](/integrations/stripe/stripe-checkout-metadata) inside `checkout` on this server request. The collector preserves them through address collection. Merchant metadata stays out of browser-facing responses and events; browser preset creates cannot set these options.

## 3. Load and mount the collector

Load the versioned element loader once on your checkout page:

```html theme={null}
<script
  type="module"
  src="https://checkout.numeralhq.com/bridge/v1.js"
></script>
```

Then create the element after your server returns the embedded session:

```html theme={null}
<div id="numeral-address-step"></div>

<script type="module">
  const response = await fetch("/api/checkout", { method: "POST" });
  const { sessionId, clientSecret } = await response.json();

  await customElements.whenDefined("numeral-checkout");

  const checkout = document.createElement("numeral-checkout");
  checkout.setAttribute("session-id", sessionId);
  checkout.setAttribute("presentation", "inline");
  checkout.setAttribute("redirect", "auto");

  // Keep the capability in memory. Do not serialize it into an HTML attribute.
  checkout.clientSecret = clientSecret;

  document.querySelector("#numeral-address-step").append(checkout);
  await checkout.open();
</script>
```

The element asks only for fields Numeral still needs. After the location is validated and tax is calculated, it redirects the buyer to the prepared Stripe Checkout Session.

## Presentation options

Set `presentation` to control how the collector appears:

| Value    | Behavior                                                    |
| -------- | ----------------------------------------------------------- |
| `inline` | Renders the collector in the element's position on the page |
| `modal`  | Opens the collector in a dialog                             |
| `button` | Renders a trigger button that opens the collector           |

Use `redirect="auto"` to continue to Stripe automatically. Use `redirect="manual"` when your application needs to navigate itself. In manual mode, listen for both redirect paths:

* `numeral-session-created` includes `detail.url` when the session can continue directly to Stripe without collecting more information.
* `numeral-before-redirect` includes `detail.url` after the embedded collector finishes collecting the required information.

## Events

The element emits bubbling, composed custom events with redacted details:

| Event                     | When it fires                                                                                                                                |
| ------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
| `numeral-session-created` | The session is loaded or its Stripe Checkout Session is created; in manual mode, includes the provider URL when no collection step is needed |
| `numeral-ready`           | The embedded collector is ready                                                                                                              |
| `numeral-state-change`    | The session state or required fields change                                                                                                  |
| `numeral-tax-calculated`  | Tax is available; includes currency and total tax amount                                                                                     |
| `numeral-before-redirect` | Immediately before navigation to Stripe; cancelable in manual flows                                                                          |
| `numeral-error`           | A safe client-facing error occurs                                                                                                            |
| `numeral-cancel`          | The buyer cancels or closes collection                                                                                                       |

When using `redirect="manual"`, handle the provider URL from either redirect path:

```js theme={null}
function redirectToProvider(event) {
  if (event.detail.url) {
    window.location.assign(event.detail.url);
  }
}

checkout.addEventListener("numeral-session-created", redirectToProvider);
checkout.addEventListener("numeral-before-redirect", redirectToProvider);
```

You can listen for the remaining lifecycle events as needed:

```js theme={null}
checkout.addEventListener("numeral-tax-calculated", (event) => {
  const { currency, totalTaxAmount } = event.detail;
  updateOrderSummary({ currency, tax: totalTaxAmount });
});

checkout.addEventListener("numeral-error", (event) => {
  showCheckoutError(event.detail.code);
});

checkout.addEventListener("numeral-cancel", () => {
  showCart();
});
```

## Appearance

The `appearance` and `locale_default` set on your checkout configuration apply inside the embedded collector as the base layer. See [Branding and appearance](/integrations/stripe/stripe-checkout-branding) for the configuration fields.

The host element supports CSS custom properties for basic visual alignment with your checkout:

```css theme={null}
numeral-checkout {
  display: block;
  --numeral-bridge-color-primary: #173f2d;
  --numeral-bridge-font-family: Inter, system-ui, sans-serif;
  --numeral-bridge-border-radius: 10px;
}
```

Host-element CSS custom properties and any `appearance` you pass to the element layer over the configuration. Variables override per key. Host rules are emitted after configuration rules, so they win without replacing them. A `locale` on the element wins over `locale_default`.

`branding.display_name`, `branding.logo`, and `branding.custom_text` are hosted-only. Your page owns its own chrome through the element's `logo`, `header`, and `footer` slots.

<Note>
  The `.TaxSummary` and `.Error` selectors accept only `borderColor`, `borderWidth`, `borderRadius`, `padding`, and `boxShadow`. This applies to host-supplied appearance as well as configuration rules: an appearance that sets `color` on either selector is dropped, so the tax total and validation errors stay legible.
</Note>

## Content Security Policy

If your site uses a Content Security Policy, allow Numeral's loader, collector frame, and client API:

```text theme={null}
script-src https://checkout.numeralhq.com
frame-src https://checkout.numeralhq.com
connect-src https://api.numeralhq.com
```

Merge these sources with your existing policy rather than replacing it.

## Security checklist

* Create Bridge sessions only from your server.
* Never expose a Numeral `sk_test_...` or live secret key in browser code.
* Assign `clientSecret` as a JavaScript property, never an HTML attribute or URL parameter.
* Do not log or persist `client_secret`.
* Allow only exact merchant origins that need to embed the collector.
* Treat collector events as lifecycle signals; buyer PII is intentionally absent.
* Reuse an idempotency key only for the same create-session request.
