Model C
Your codes stay in your system. At checkout Brizz calls an endpoint you host; your answer decides whether the discount applies. This is the only model where you run a service.
You build an HTTPS verification endpoint
Direction of travel
In Models A and B you call Brizz. In Model C Brizz calls you — you are the server. Same request shape, very different operational commitment: this endpoint sits inside a live checkout with a customer waiting on it.
Content-Type: application/json
User-Agent: Brizz-Verification/1.0
Authorization: Bearer <secret> # or: X-API-Key: <secret>
# if HMAC signing is enabled:
X-Brizz-Timestamp: 1784654651
X-Brizz-Request-Id: <uuid>
X-Brizz-Signature: v1=<hex>
{ "code": "PARTNER-AB12", "campaign_uuid": "…", "request_id": "<uuid>" }No customer PII leaves Brizz. You issued the code to a specific person, so the code is already your user reference — you can answer “is this valid, and is this user new to us” from your own records alone.
Keep request_id in your logs. It is the handle Brizz support uses to correlate one specific call with your side, and it is in the body of every request — the X-Brizz-Request-Id header carries the same value but is only sent when signing is enabled.
signed_payload = "{timestamp}.{request_id}.{raw_body}"
expected = HMAC_SHA256(your_hmac_secret, signed_payload)Use the raw body exactly as received — do not re-serialise the JSON. Compare in constant time against any v1= candidate (multiple appear only during rotation), and reject if |now − timestamp| > 300 s.
Exactly HTTP 200, application/json, at most 64 KiB. Anything else — another status, a non-JSON body, a timeout — is treated as unavailable: no discount, and checkout continues normally.
# approve
{ "approved": true, "authorization_ref": "ptr-9f2c…", "reason": null }
# deny
{ "approved": false, "reason": "ALREADY_USED" }approved — Required, strict JSON boolean. A truthy string or number is rejected as malformed.authorization_ref — Optional on approval. Opaque, ≤128 characters, no PII. Over-length keeps the approval but drops the reference — we never truncate it, because a truncated identifier can collide.reason — Optional, ≤256 chars, resolved against a CLOSED vocabulary: INVALID · EXPIRED · ALREADY_USED · NOT_ELIGIBLE · REVOKED. Matching is forgiving about case, whitespace, hyphens and dots, so “already used” and “Already-Used” both land on ALREADY_USED. Never shown to the customer.money fields — discount, amount, price, value, discount_eur and discount_cents are read for a bounded metric and then discarded — never persisted, and never used to price anything. Brizz prices from the campaign's own budget config, under a lock. Your endpoint answers yes or no; it cannot set the discount.A reason outside the list is hashed, not stored
Anything that does not resolve to one of the five is replaced with OTHER_<12 hex>, a keyed digest of the string you sent. Your raw text never survives into results, cache, logs or metrics — so a free-text reason cannot carry customer data into Brizz through this field, but you also lose the ability to read it back. Use the vocabulary.
POST /verification:
1. authenticate
if header(Authorization) != "Bearer " + OUR_SECRET → 401 (constant-time)
2. verify signature, if you enabled HMAC
payload = timestamp + "." + request_id + "." + raw_body
if no v1= candidate matches HMAC_SHA256(secret, payload) → 401
if abs(now - timestamp) > 300 → 401
3. look the code up in YOUR store
coupon = find(body.code)
if !coupon → 200 { approved: false, reason: "INVALID" }
if coupon.used → 200 { approved: false, reason: "ALREADY_USED" }
if coupon.expired → 200 { approved: false, reason: "EXPIRED" }
4. approve
return 200 { approved: true, authorization_ref: coupon.id }Do not mark the code used here
A verification call is not a redemption — the customer may still abandon checkout. If you burn the code on verify, an abandoned basket consumes it permanently. Mark it used when you see the redemption in reconciliation, or treat your authorization_ref as a reservation and settle later.
Every failure resolves the same way for the customer: no discount, full price, checkout continues. What changes is what Brizz does next.
Five failures inside a 300-second window trip the breaker; any success in between clears the count. Once tripped, Brizz stops calling you for a 60-second cooldown and denies locally. After the cooldown exactly one request probes you — everything else keeps failing closed until that probe resolves, and a failed probe re-opens the breaker immediately.
What the single probe buys you
Recovering from an outage will never hit you with the backlog at once. You get one request, and normal traffic resumes only if it succeeds. The breaker is keyed per endpoint, so one partner's outage never affects another's.
We never receive or hold your code universe — we only learn a code when a customer actually types it. On approval Brizz materialises that single code as one row, marked as originating from real-time verification, carrying your authorization_ref. That row is the only double-spend defence there is: without it there is nothing to lock, so two concurrent checkouts could each spend the same code. It is also what lets the existing budget-cap, per-user-limit, webhook and reporting machinery treat your codes exactly like any other.
Before that, during verification itself, nothing is written to the database at all, and the short-lived approval cache is keyed by a keyed hash — never by your raw code. Any change to your endpoint configuration discards cached approvals immediately rather than waiting for them to expire.
Brizz runs a synthetic Test connection against your endpoint. It uses the same transport, the same request body and the same signing as production, so it exercises the full wire — URL, TLS, auth, HMAC and response shape — with the code BRIZZ-CONNECTION-TEST, prefixed with your route's prefix when one is configured.
That code cannot match anything on the Brizz side, so answering approved: false still passes. Your approve/deny answer is discarded entirely; only reachability and protocol shape are reported.
Safe to run against production
Test connection does not go through the live verification path. It writes no preview cache and cannot move your circuit breaker, so a failing test can never affect real checkouts and a passing one can never seed a live approval. It mints no code, creates no reservation or booking, moves no budget and takes no payment.
Brizz uses cookies to improve your experience and measure site usage. You can manage your preferences at any time.