Brizz

Model B

You upload your codes

You already have a batch of codes. Give them to Brizz once — by CSV, or one API call — and checkout matches them locally with no live dependency on your systems.

You build a CSV, or one API call

Machine-readable contract

The full Partner API is published as OpenAPI at /coupon-integration/partner-api-v1.yaml — load it into Swagger or Redoc, or generate a client from it.

Flow

PARTNERBRIZZEND USERAHEAD OF TIMEAT CHECKOUT — THE CUSTOMER IS WAITINGYou generate codesStores your codesGets code from youEnters at checkoutMatched locallyDiscount appliedPays lessupload once — CSV or APIno partner involvement at checkout
One upload ahead of time, then the partner lane goes quiet. From checkout onward this is identical to Model A — only the origin of the codes differs.

What you need from your side

  • RequiredYour list of codesThe only hard requirement. Send it to your Brizz campaign contact as a CSV — a Brizz operator imports it through the internal admin dashboard, with no engineering involvement at all.
  • OptionalAn API key with codes:writeOnly if you would rather import programmatically than upload a file. It is one HTTP call.
  • OptionalA webhook endpointOnly if you would rather not poll. Receives code.redeemed, signed with a secret we issue.

No developer needed

Send the CSV to your Brizz contact and you are done. Once codes are being used, your team signs in at brizz.me/partners with a code sent to their work email, to follow redemptions, download them as CSV and see your statements. Everything below is for teams that want to automate this.

Authentication

Brizz issues your API key and gives it only the permissions you need — a key that can only check codes can never create them. The secret is shown once, when the key is created. Brizz keeps only a one-way hash of it, so a lost secret cannot be recovered, only replaced. Keys can be rotated or revoked at any time.

Base URL and credential
# Base URL
https://api.brizz.me/api/partner/v1

# Every request — this header only, no alternative accepted
Authorization: Bearer brz_partner_<credential_id>.<secret>

Every auth failure looks the same

A missing key, a wrong key, a revoked key and a key without the permission all return the same 401. Nothing tells them apart, so someone guessing keys or permissions learns nothing. If a call you expect to work returns 401, check the key's permissions with your Brizz contact.

Endpoints

GET /campaigns
What it does: Your campaigns, their status and code counts
Permission: campaigns:read
Limit: 300/min
POST /campaigns/{uuid}/codes
What it does: Upload your codes, or have Brizz create them
Permission: codes:write
Limit: 10/min
POST /codes/status
What it does: Check one code
Permission: codes:read
Limit: 300/min
GET /campaigns/{uuid}/redemptions
What it does: Redeemed and refunded codes, for reconciliation
Permission: redemptions:read
Limit: 30/min

On top of each call's own limit, all of your keys together share 600 calls a minute, and any one address gets 60 calls a minute before its key is checked. Over a limit, you get 429.

Importing your codes

One call does both: send codes to upload your own — up to 5,000 per call — or quantity to have Brizz create them, which is Model A. Send exactly one of the two. Codes are uppercased, and each must be 4–32 letters, digits or hyphens.

POST /api/partner/v1/campaigns/{uuid}/codes
# permission: codes:write
Authorization: Bearer brz_partner_<credential_id>.<secret>
Idempotency-Key: 9a1b…

{
  "codes": ["PARTNER-AB12", "PARTNER-CD34", "PARTNER-CD34"]
}

# ← 200
{
  "status": "success",
  "message": "CODES_MINTED",
  "imported": 2,
  "skipped": [{ "code": "PARTNER-CD34", "reason": "DUPLICATE_IN_FILE" }],
  "codes": ["PARTNER-AB12", "PARTNER-CD34"],
  "idempotent_replay": false
}

Anything not imported comes back in skipped with its reason, so nothing is dropped silently: DUPLICATE_IN_FILE (you sent it twice), ALREADY_EXISTS (it is already in Brizz) or INVALID_FORMAT.

Idempotency-Key is required — give each batch its own. If a call times out, retry with the same key: you get the original result back, marked "idempotent_replay": true and with skipped_count in place of the list, and nothing is imported twice.

Checking a code

POST /api/partner/v1/codes/status
# permission: codes:read
# →
{ "code": "PARTNER-AB12" }

# ← 200
{
  "code": {
    "status": "REDEEMED",
    "redeemed_at": "2026-07-22T18:04:11+00:00",
    "campaign_uuid": "…"
  }
}

A code goes AVAILABLE → RESERVED while someone is at checkout with it → REDEEMED once they pay, and becomes REFUNDED if the booking is refunded. A checkout that is never paid puts the code back to AVAILABLE. No buyer identity is ever returned — only the status, the time and the campaign.

Webhooks

Rather than poll, subscribe. Brizz pushes code.redeemed to your URL, signed so you can verify authenticity. Delivery is at-least-once with retry and backoff — dedupe on the event id.

Webhook headers
X-Brizz-Timestamp: 1784654651        # unix seconds
X-Brizz-Event-Id:  <uuid>            # dedupe key
X-Brizz-Signature: v1=<hex>[,v1=<hex>]

# HMAC-SHA256 over the exact transmitted bytes:
#   "{timestamp}.{event_id}.{raw_body}"

The dual v1=,v1= form appears only during a signing-secret rotation: the previous secret stays valid for a 24-hour grace window, so you can switch over with no dropped deliveries.

Webhook body
{
  "event": "code.redeemed",
  "event_id": "<uuid>",
  "occurred_at": "2026-09-14T18:04:12+00:00",
  "data": {
    "code": "PARTNER-AB12",
    "campaign_uuid": "…",
    "redeemed_at": "2026-09-14T18:04:11+00:00",
    "discount_eur": "10.00"
  }
}

data.billed_eur is added only when what you are invoiced differs from the discount, because you agreed a fixed amount per redemption. data.authorization_ref is added only for a Model C code your endpoint gave a reference. No buyer identity is ever sent.

Reply with any 2xx. Anything else is retried — seven attempts in all, after 1 minute, 5 minutes, 15 minutes, 1 hour, 3 hours and 12 hours, so about 16 hours end to end. Each retry carries a fresh timestamp and signature but the same event id.

Reconciliation reports

Every campaign has a report of what was redeemed and what you owe. Your team can download it at any time from the partner portal, and Brizz can email it to you daily, weekly or monthly, with a final one when the campaign ends. The API returns the same rows:

GET /api/partner/v1/campaigns/{uuid}/redemptions?from=2026-09-01&to=2026-09-30
# permission: redemptions:read · ← 200
{
  "status": "success",
  "campaign_uuid": "…",
  "window": { "from": "2026-09-01T00:00:00+00:00", "to": "2026-09-30T23:59:59+00:00" },
  "rows": [
    {
      "code": "PARTNER-AB12",
      "status": "REDEEMED",
      "redeemed_at": "2026-09-14T18:04:11+00:00",
      "event_title": "…",
      "event_uuid": "…",
      "order_subtotal_eur": "40.00",
      "discount_eur": "10.00",
      "billed_eur": "10.00",
      "booking_ref": "…",
      "authorization_ref": ""
    }
  ],
  "next_cursor": null,
  "totals": {
    "redeemed_count": 1,
    "refunded_count": 0,
    "invoice_total_eur": 10,
    "settlement": {
      "delivered_brutto_eur": 10,
      "net_eur": 8.4,
      "vat_rate": 19,
      "vat_eur": 1.6,
      "invoiced_total_eur": 10
    }
  }
}
code
The code that was used.
status
REDEEMED, or REFUNDED if the booking was refunded afterwards.
redeemed_at
When it was redeemed, in UTC.
event_title, event_uuid
The event the booking was for.
order_subtotal_eur
The booking's subtotal before any discount.
discount_eur
What the buyer saved with this code.
billed_eur
What you are invoiced for this code. The same as discount_eur, unless you agreed a fixed amount per redemption.
booking_ref
Brizz's booking number, to quote to us. Never the buyer's name or email.
authorization_ref
Model C only: the reference your endpoint returned. Empty for Models A and B.

totals covers the whole window, not just the page you are on: invoice_total_eur is the sum of billed_eur over redeemed codes, with refunds left out, and settlement splits that same amount into net and VAT.

from and to are required, in UTC, and at most 92 days apart; a date on its own covers the whole day. A page holds up to 500 rows (100 by default) — pass next_cursor back as cursor until it comes back null.

Brizz uses cookies to improve your experience and measure site usage. You can manage your preferences at any time.