> For the complete documentation index, see [llms.txt](https://developer.branta.pro/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://developer.branta.pro/setup/parent-platforms.md).

# Parent Platforms

A **parent platform** is a service that integrates Branta on behalf of many merchants and `POST`s payments for each — typically a multi-tenant payment app like [Zaprite](/setup/platforms/payment-gateway-options/zaprite.md) or [Take My Sats](/setup/platforms/payment-gateway-options/take-my-sats.md). Each merchant has their own Branta platform and their own API key. The parent platform holds an HMAC secret and uses it to sign every payment publish, proving the request actually originated from it and not from someone who got a copy of a merchant's API key.

{% hint style="info" %}
Single-tenant or self-hosted deployments like [BTCPay Server](/setup/platforms/payment-gateway-options/btcpay-server.md) are **not** parent platforms — each BTCPay store is its own Branta platform with its own API key, and there is no HMAC layer. Parent Platforms apply when one piece of software is calling Branta on behalf of many separately-onboarded merchants.
{% endhint %}

### How requests are authenticated

Every payment publish from a parent platform carries two credentials:

1. **The merchant's API key** in `Authorization: Bearer …`. This tells Branta which merchant's platform the payment belongs to, so the right name and logo render to senders.
2. **The parent's HMAC signature** in `X-HMAC-Signature` and `X-HMAC-Timestamp`. The merchant's API key is configured with a `parent_platform_id`, which tells Branta to validate the request's HMAC against the parent's active API secrets. Requests missing a valid signature are rejected.

The merchant onboards as a normal [Platform](/setup/platforms.md) and, when generating their API key in Guardrail, selects your parent platform from the dropdown. From then on, that key only works when accompanied by your HMAC signature.

### What you get

* **Per-merchant counterparty rendering.** Each merchant appears in the sender's wallet with their own name and logo, with your parent platform shown as the hosting service.
* **One HMAC secret, regardless of how many merchants you serve.** Each merchant has their own API key; you hold a single signing secret that authorizes your publishes on their behalf.
* **Same privacy options as a regular platform** — plain or [Zero-Knowledge](/tech/api/v2/adding-payments.md#zero-knowledge), per destination.
* A listing in the [Branta network directory](https://branta.pro/network) once you open a PR on [branta-network](https://github.com/BrantaOps/branta-network).

### Onboarding

#### 1. Create an account

Sign up at [guardrail.branta.pro](https://guardrail.branta.pro). For testing use the staging dashboard at [staging.guardrail.branta.pro](https://staging.guardrail.branta.pro/session/new). Staging and production are siloed — see [Environments](/tech/environments.md).

#### 2. Submit a Platform Request

In Guardrail, create a Platform Request. A Branta admin reviews it (a light KYB check) and approves the platform on your account.

#### 3. Get parent platform access enabled

A Branta admin needs to turn this on for your account. Reach out and ask — without it, the API Secrets section in Guardrail will not be available, and merchants won't be able to select you as a parent when issuing their API keys.

#### 4. Generate an API Secret

Once parent platform access is enabled, an **API Secrets** section appears in Guardrail. Generate a secret there.

The secret is shown **once at creation** — copy it immediately and store it server-side (your secret manager, not source control).

{% hint style="danger" %}
The API Secret is the HMAC signing material for every payment you publish on behalf of every merchant. Treat it like a root credential — store it server-side only, never ship it to clients, and rotate immediately if exposed.
{% endhint %}

You can hold multiple active secrets at the same time, which makes rotation safe: generate the new one, deploy it, then revoke the old one once nothing is signing with it.

#### 5. Integrate

For each merchant you serve:

1. They onboard as a normal [Platform](/setup/platforms.md) (steps 1–3 of that flow), then create an API key in their own Guardrail account and select your parent platform from the dropdown.
2. They share that API key with you (the same way they'd share an API key with any payment service they use).
3. Your service `POST`s their payment destinations to Branta using their API key in `Authorization`, signed with your HMAC secret.

The request body is identical to a regular platform publish (see [Adding Payments](/tech/api/v2/adding-payments.md)); the only difference is the two extra headers:

* `X-HMAC-Signature` — hex SHA-256 HMAC of the canonical message string, keyed by your API Secret.
* `X-HMAC-Timestamp` — Unix epoch seconds. Branta rejects requests outside a 30-minute window.

The canonical message is the request method, full URL, raw body, and timestamp joined with literal `|` characters, no whitespace:

```
POST|https://guardrail.branta.pro/v2/payments|{json-body}|1716998400
```

The SDKs handle this signing automatically when you supply the merchant's API key and your HMAC secret in their options. See [SDK](/tech/sdk.md) for the canonical reference; a minimal JS example:

```ts
import { BrantaServerBaseUrl } from "@branta-ops/branta";
import { BrantaService } from "@branta-ops/branta/v2";
import { DestinationType } from "@branta-ops/branta";
import { PrivacyMode } from "@branta-ops/branta";

// One service instance per merchant — the API key changes per merchant,
// the HMAC secret is yours and stays constant.
const service = new BrantaService({
  baseUrl: BrantaServerBaseUrl.Production,
  defaultApiKey: merchant.brantaApiKey,         // merchant's key
  hmacSecret: process.env.BRANTA_API_SECRET!,   // your secret
  privacy: PrivacyMode.Loose,
});

await service.addPayment({
  description: "Order #4501 — Acme Coffee",
  destinations: [
    {
      value: "bc1qu3k6geqdjncaarsu2vq56tt8php5vsug9kasmq",
      type: DestinationType.BitcoinAddress,
      isPrimary: true,
      isZk: false,
    },
  ],
});
```

If you'd rather sign requests yourself, a raw `curl`:

```bash
BODY='{"description":"Order #4501","destinations":[{"value":"bc1q...","type":"bitcoin_address","primary":true,"zk":false}]}'
TS=$(date +%s)
MSG="POST|https://guardrail.branta.pro/v2/payments|${BODY}|${TS}"
SIG=$(printf '%s' "$MSG" | openssl dgst -sha256 -hmac "$BRANTA_API_SECRET" | awk '{print $2}')

curl -X POST https://guardrail.branta.pro/v2/payments \
  -H "Authorization: Bearer ${MERCHANT_API_KEY}" \
  -H "Content-Type: application/json" \
  -H "X-HMAC-Signature: ${SIG}" \
  -H "X-HMAC-Timestamp: ${TS}" \
  -d "$BODY"
```

For the full request/response schema (destination types, ZK fields, response shape) see the [Adding Payments](/tech/api/v2/adding-payments.md) reference.

{% hint style="info" %}
If you publish ZK on-chain destinations (`isZk: true`), the QR code you render at checkout must include `branta_id` and `branta_secret` query parameters so wallets can look up and decrypt the record. See [QR code / payment URI](/setup/platforms/custom-integration.md#qr-code-payment-uri-zk-on-chain) in the Custom Integration guide.
{% endhint %}

### After you're live

* **Test against staging first.** Use a staging API Secret from [staging.guardrail.branta.pro](https://staging.guardrail.branta.pro) and a staging merchant API key, and confirm payments resolve on [scan.branta.pro](https://scan.branta.pro/scan).
* **Get listed.** Open a PR on [branta-network](https://github.com/BrantaOps/branta-network) to appear as a parent platform in the public [partner directory](https://branta.pro/network).
* **Rotate on exposure.** Generate a new secret, deploy it, then revoke the old one from Guardrail. Multiple active secrets can run side-by-side during the cutover.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://developer.branta.pro/setup/parent-platforms.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
