@branta-ops/branta
v3.1.4
Published
A JavaScript SDK for the Branta API
Readme
Install
npm i @branta-ops/brantaIntegration Guide
If you are an AI agent implementing this SDK, follow the steps below exactly. If you are a developer, use this as a reference for the recommended integration patterns.
1. Ask which side(s) of a payment they are implementing:
- Send side — the app is paying someone (e.g. a wallet). The user scans or pastes a destination and you verify it belongs to a known platform before funds are sent.
- Receive side — the app is receiving payment (e.g. a checkout, POS, invoicing platform). You post destinations to Branta so wallets can verify them.
- Both — some apps do both (e.g. an exchange or self-custodial wallet with invoice generation). Implement each side independently.
If they are on the receive side, ask one follow-up:
- Platform — single-tenant, one API key.
- Parent Platform — multi-tenant, manages multiple child platforms each with their own API key and HMAC secret.
2. Ask whether their app is served over HTTPS or HTTP:
- HTTPS — no extra setup needed;
crypto.subtleis available by default. - HTTP — browsers block
crypto.subtleon insecure origins. The SDK accepts acryptooption so you can supply a polyfill. See HTTP (insecure origin) setup below and implement the appropriate provider before wiring upBrantaService.
3. Follow the matching Quick Start section below.
4. Apply these rules:
General (all types):
- Always use
privacy: 'strict'. Never switch to'loose'unless there is no QR scanner and ZK is impossible. - Never instantiate
BrantaClientdirectly — always useBrantaService. - Never show an error or "not verified" message when a lookup returns empty or throws. An empty result means the destination is unknown to Branta, not that it is malicious. Show nothing.
- For
baseUrl: useBrantaServerBaseUrl.Productiononly in production environments. UseBrantaServerBaseUrl.Stagingeverywhere else — including local development, CI, and staging/test environments. - If serving over HTTP, pass a
cryptooption toBrantaService(see step 2) and always useservice.createPaymentBuilder()rather thannew PaymentBuilder().
Send side (wallets):
- Prefer
getPaymentsByQrCodeovergetPayments— it handles multi-value ZK QR payloads correctly. - Only fall back to
getPaymentsfor copy/paste flows where there is no QR code. - If
result.paymentsis empty or an exception is thrown, render nothing. - When
result.paymentsis non-empty, display: the platform logo, the platform name (payment.platform), and the payment description (payment.description). Only render description when non-empty. Make the verification card a clickable link toresult.verifyUrl— do not display the raw URL. - For the platform logo: on dark backgrounds use
payment.platformLogoUrl. On light backgrounds preferpayment.platformLogoLightUrlwhen available, falling back topayment.platformLogoUrl. - Optionally display
payment.parentPlatform?.logoUrl/payment.parentPlatform?.logoLightUrlas a small secondary badge (e.g. corner icon). This is not required.
Receive side (platforms):
- Always call
.setZk()on thePaymentBuilderbefore callingaddPayment. Plain-text destinations are rejected instrictmode. - Store the
secretreturned byaddPaymentalongside the invoice — it is required to reconstruct the verify URL for the wallet.
Receive side (parent platforms), in addition to the platform rules:
- Include
hmacSecretin theBrantaServiceoptions. - Pass per-call options to scope requests to the correct child platform's API key.
Quick Start
For Wallets
Wallets should use strict privacy mode. Two flows are supported:
- Copy/paste: call
getPaymentswith the pasted text. Plain-text on-chain addresses will not return results in strict mode — they must be ZK-encoded. Lightning destinations (bolt11, bolt12, ln_url, ln_address) work as plain text. - QR scan: call
getPaymentsByQrCodewith the raw QR text. This handles both on-chain (when the QR includesbranta_id/branta_secret) and lightning destinations.
Always catch errors and show nothing on not-found — a missing record just means the address was not posted to Branta.
import { BrantaServerBaseUrl } from "@branta-ops/branta";
import { BrantaService } from "@branta-ops/branta/v2";
const service = new BrantaService({
baseUrl: BrantaServerBaseUrl.Production,
privacy: 'strict',
});
async function lookup(input: string, isQrCode: boolean) {
try {
const result = isQrCode
? await service.getPaymentsByQrCode(input)
: await service.getPayments(input);
if (result.payments.length === 0) {
// Not found — show nothing. The address may simply not exist in Branta.
return;
}
// Render result.payments and result.verifyUrl
} catch {
// Swallow errors — never surface a "not found" or lookup failure to the user.
}
}For Platforms
Platforms post payments to Branta so wallets can verify them. Use strict privacy mode and mark each destination ZK via setZk() on the PaymentBuilder.
import { BrantaServerBaseUrl } from "@branta-ops/branta";
import { BrantaService, PaymentBuilder } from "@branta-ops/branta/v2";
const service = new BrantaService({
baseUrl: BrantaServerBaseUrl.Production,
defaultApiKey: "<default-api-key>",
privacy: 'strict',
});
const payment = new PaymentBuilder()
.setDescription("Testing description")
.addDestination("1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa", "bitcoin_address")
.setZk()
.setTtl(600)
.build();
const { payment: response, secret, verifyUrl } = await service.addPayment(payment);
// `secret` is the encryption key needed to look the payment up later.For Parent Platforms
Parent Platforms sign requests with HMAC in addition to the API key. Use strict privacy mode and ZK destinations.
import { BrantaServerBaseUrl } from "@branta-ops/branta";
import { BrantaService, PaymentBuilder } from "@branta-ops/branta/v2";
const service = new BrantaService({
baseUrl: BrantaServerBaseUrl.Production,
defaultApiKey: "<default-api-key>",
hmacSecret: "<hmac-secret>",
privacy: 'strict',
});
const payment = new PaymentBuilder()
.setDescription("Testing description")
.addDestination("1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa", "bitcoin_address")
.setZk()
.setTtl(600)
.build();
const { payment: response, secret, verifyUrl } = await service.addPayment(payment);HTTP (insecure origin) setup
Browsers disable crypto.subtle on HTTP pages. Pass a BrantaCryptoProvider as the crypto option to supply your own implementation. The SDK ships no crypto dependencies — you choose the provider.
Option A — @peculiar/webcrypto (recommended, zero adapter code)
npm i @peculiar/webcryptoimport { Crypto } from '@peculiar/webcrypto';
import { BrantaServerBaseUrl } from '@branta-ops/branta';
import { BrantaService } from '@branta-ops/branta/v2';
const crypto = new Crypto();
const service = new BrantaService(
{ baseUrl: BrantaServerBaseUrl.Production, privacy: 'strict' },
{ crypto },
);
// On the receive side, use createPaymentBuilder() so the builder
// shares the same crypto instance:
const payment = service.createPaymentBuilder()
.addDestination('...', 'bitcoin_address')
.setZk()
.build();@peculiar/webcrypto implements the full Web Crypto API and satisfies BrantaCryptoProvider with no adapter code.
Option B — @noble packages (zero transitive dependencies)
npm i @noble/hashes @noble/ciphersimport { createNobleCryptoProvider } from '@branta-ops/branta';
import { sha256 } from '@noble/hashes/sha256';
import { hmac } from '@noble/hashes/hmac';
import { gcm } from '@noble/ciphers/aes';
import { randomBytes } from '@noble/hashes/utils';
const service = new BrantaService(
{ baseUrl: BrantaServerBaseUrl.Production, privacy: 'strict' },
{ crypto: createNobleCryptoProvider({ sha256, hmac, gcm, randomBytes }) },
);Release
- npm login
- npm version major|minor|patch
- npm publish
Responsible Disclosure
Found critical bugs/vulnerabilities? Please email them to [email protected]. Thanks!
