Skip to content

Five-minute integration

Ask us for a partner key for your brand. It looks like bm_live_…, and it comes with scopes:

scopelets you
voucher.readlook up a voucher and read its sealed event chain
voucher.redeemreserve, redeem and release
Terminal window
npm install @bullmark/sdk
import { Bullmark } from '@bullmark/sdk';
const bm = new Bullmark({ apiKey: process.env.BULLMARK_API_KEY! });
// A customer gives you a code at checkout.
const { voucher } = await bm.vouchers.inspect(code);
if (voucher.status !== 'Active') {
throw new Error('that voucher cannot be used right now');
}
// Charge them voucher.priceUsd instead of your list price, then:
const receipt = await bm.vouchers.redeem({
code,
externalRef: order.id, // YOUR order id
});

That is the whole integration. receipt.verifyUrl is a public link that proves the redemption is on the chain — store it with the order if you like.

externalRef is your own order or invoice reference, and it is required, because it is the idempotency key.

// ✅ the SAME reference on every attempt
await retry(() => bm.vouchers.redeem({ code, externalRef: order.id }));
// ❌ a fresh reference per attempt — a timeout now costs your customer a second voucher
await retry(() => bm.vouchers.redeem({ code, externalRef: randomUUID() }));

If your HTTP client times out and retries, the same reference returns the first result with alreadyConfirmed: true. That is a success, not an error — do not surface it as one.