# Quickstart: Embedded payments SDK

Mount the Syntch payment form inside your own checkout page with the browser loader.

**Category:** Quickstart

**Last reviewed:** 20 August 2026

# Quickstart: Embedded payments SDK

The payer stays on your checkout page and the card fields are rendered inside a frame Syntch serves. Your page keeps its own layout and flow, and neither your page nor your server ever touches a card number.

This is the redirect flow's sibling: the same page and the same session, mounted in place instead of navigated to. Set up the hosted page first, then come back here.

You need a hosted page id, an API key, and the origin your checkout is served from.

## 1. Authorize your origin to embed

Two settings have to line up, and a mismatch is the single most common reason an embed shows nothing.

Add your checkout's domain to the hosted page's **allowed embedding domains** in the portal. Wildcards such as `*.example.com` work; raw IP addresses, `localhost`, and entries with no top-level domain are rejected.

Then name the exact origin on every session you create. If the origin is omitted the session still works for a redirect, but the message channel the loader depends on stays closed, and your page receives nothing.

## 2. Create the session on your server

```bash
curl -X POST "https://your-gateway-host/api/hostedpaymentpages/sessions" \
  -H "api-key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "hostedPageId": "{hostedPageId}",
    "label": "Order 1042",
    "parentOrigin": "https://checkout.example.com",
    "amountMode": "Locked",
    "prefilledFields": { "base_amount": "10.00" },
    "expirySeconds": 900
  }'
```

The response carries `hppUrl`, the absolute address the frame will point at, already resolved against the payment host. Hand that value back to your page. There's no second call to make for it:

```json
{
  "sessionId": "3f1c...",
  "shortToken": "aB3kX9mZqR7T",
  "hppUrl": "https://pay.your-environment.example/pay/s/aB3kX9mZqR7T",
  "expiresAt": "2026-01-01T18:30:00Z",
  "label": "Order 1042"
}
```

Keep the call on the server. The API key must never reach the browser: a key in page source is a credential anyone who views source can use.

## 3. Add the script

```html
<script src="https://pay.your-environment.example/sdk/v1/pay.js"></script>
```

The loader is a few hundred bytes. It resolves the release your payment host is actually serving, injects the payment code with its integrity attribute already applied, and assigns a window global.

The name of that global is a per-deployment value, so no single name this page could print would be right everywhere. Your administrator reads it off the branding settings. The examples below call it `PaySdk`:

```js
var PaySdk = window.YourConfiguredGlobalName;
```

`https://pay.your-environment.example` is a placeholder and doesn't resolve. Your administrator has the payment host for your environment.

## 4. Mount the form

```html
<div id="checkout"></div>

<script>
  var payment = PaySdk.mount('#checkout', {
    sessionUrl: SESSION_URL_FROM_YOUR_SERVER,

    onComplete: function (outcome) {
      window.location = '/order/confirmed?ref=' + encodeURIComponent(outcome.transactionId);
    },
    onFailed: function (outcome) {
      showMessage(outcome.responseMessage || outcome.reason || 'That payment was not approved.');
    },
    onCancelled: function () {
      window.location = '/cart';
    },
    onSessionInvalid: function () {
      showMessage('This checkout is no longer usable. Start again to get a fresh one.');
    }
  });
</script>
```

The container is an element or a CSS selector. A selector matching nothing throws straight away, on the page that set it up, rather than failing in front of a payer with no explanation.

In a single-page application, call `payment.destroy()` when the view unmounts. Leaving the frame and its message listener attached across a route change is the reliable way to end up with two mounts competing for one page.

## 5. Confirm from the webhook, not from the callback

`onComplete` is what you show the person looking at the screen. The webhook is the record. A browser that loses its connection between the approval and the callback leaves your page with nothing and your server with a real payment, so the reconciliation path has to be the delivery your server receives.

## Next steps

- [Quickstart: Webhooks](/help/guides/quickstart-webhooks) sets up the receiver that confirms these payments.
- [The embedded payments SDK](/help/guides/embedded-payments-sdk) is the full reference: every option, every event, script pinning, and the Content Security Policy your page needs.
- [Embedded payments compared with direct card scripts](/help/guides/embedded-payments-vs-direct-card-scripts) covers why the card fields live in a frame.

## You need credentials to run this

Every request on this page sends an API key. Ask your Syntch contact for access to a sandbox merchant, then create a key against it in the application. A key is shown in full once, when you create it.

This instance publishes no support address. Whoever provisioned your access can issue the sandbox merchant.

## See also

- [All documentation](https://devportal-simpay-sbx.winkpg.io/llms.txt): the machine-readable index of every public page on this site.
