Bullmark.Sdk (.NET)
dotnet add package Bullmark.Sdk.NET 10. Server-side only — the key burns vouchers.
var bm = new BullmarkClient(new BullmarkOptions{ ApiKey = config["Bullmark:ApiKey"]!, BaseUrl = "https://api.bullmark.net", // optional});Register it once — it is thread-safe and holds no per-request state. Pass an HttpClient from
IHttpClientFactory so its lifetime and pooling are yours:
services.AddHttpClient("bullmark");services.AddSingleton(sp => new BullmarkClient( new BullmarkOptions { ApiKey = cfg["Bullmark:ApiKey"]! }, sp.GetRequiredService<IHttpClientFactory>().CreateClient("bullmark")));The four calls
Section titled “The four calls”await bm.Vouchers.InspectAsync(codeOrTicket, ct); // what do I owe them?await bm.Vouchers.ReserveAsync(code, ct); // hold it (optional)await bm.Vouchers.RedeemAsync(new RedeemRequest( // burn it ExternalRef: order.Id.ToString(), Code: code), ct);await bm.Vouchers.ReleaseAsync(ticketOrCode, "payment_failed", ct); // give it backawait bm.Integrity.ChainAsync(code, ct); // check usRedeemAsync throws ArgumentException before making a request when ExternalRef is
missing — a missing reference is otherwise only discovered when a timeout costs a customer
their voucher.
Errors
Section titled “Errors”catch (BullmarkException ex) when (ex.Unreachable){ // We never got an answer. Do NOT tell the customer their voucher is invalid.}catch (BullmarkException ex){ // ex.Message — our own words, written for a person. Show it. // ex.Status — the HTTP status, if you need to branch.}Verifying without taking our word for it
Section titled “Verifying without taking our word for it”ChainAsync returns the sealed chain with canonical payloads, so you can recompute the
hashes locally. Axowl.Sdk.Integrity.Hashing computes the same document; the algorithm and
its conformance vectors are on The seal hash.