enfinitos-sdk-brand-go
EnfinitOS Brand/Advertiser SDK — a lightweight Go client that lets a brand (advertiser) query its own delivery proof, metering, and settlement records directly, without going through the operator's reporting plane. Read-only by design and scoped to campaigns the brand owns.
Go counterpart of @enfinitos/sdk-brand (TypeScript) and enfinitos_brand (Python). Same wire shape, idiomatic Go: context.Context first, stdlib net/http, zero third-party dependencies.
Who should use this
You are a brand (advertiser) and:
- your data pipeline lives in Go (microservice, AR/finance batch job, Kubernetes controller, …);
- you need to reconcile EnfinitOS invoices against your own impression logs or auditor analysis;
- you want to file disputes backed by signed counter-evidence the operator is contractually bound to respond to.
If you are an operator, you want the operator-web SDK instead. If you are an auditor verifying signatures, you want enfinitos-sdk-auditor-go for the cryptographic primitives. The brand SDK pulls the data; the auditor SDK verifies it.
Authentication
Authorization: Bearer brk_…
X-Enfinitos-Brand: brand_acme
Brand API keys are issued by EnfinitOS to the brand's tenant admin via the brand portal. They are scoped read-only to records the brand owns. Disputes are the only write surface and are bound to the brand's own auditor-key signature, not the API key.
Installation
go get github.com/enfinitos/ar-campaign-os/packages/sdks/brand-go
Requires Go 1.21+. The SDK depends only on the Go standard library.
Getting started
package main
import (
"context"
"fmt"
"log"
"os"
"time"
brand "github.com/enfinitos/ar-campaign-os/packages/sdks/brand-go"
)
func main() {
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
client, err := brand.NewEnfinitOSBrandClient(brand.ClientOptions{
APIBaseURL: "https://api.enfinitos.com",
BrandID: os.Getenv("ENFINITOS_BRAND_ID"),
APIKey: os.Getenv("ENFINITOS_BRAND_API_KEY"),
})
if err != nil {
log.Fatal(err)
}
// 1. List active campaigns.
page, err := client.Campaigns.List(ctx, brand.CampaignsListOptions{
Status: brand.CampaignActive,
})
if err != nil {
log.Fatal(err)
}
fmt.Printf("Brand has %d active campaigns\n", len(page.Items))
// 2. For each, pull the signed proof pack + metering summary.
for _, cmp := range page.Items {
pack, err := client.Proof.Pack(ctx, cmp.CampaignID)
if err != nil {
log.Printf("proof pack %s: %v", cmp.CampaignID, err)
continue
}
summary, err := client.Metering.Summary(ctx, cmp.CampaignID)
if err != nil {
log.Printf("metering summary %s: %v", cmp.CampaignID, err)
continue
}
fmt.Println(cmp.Name, summary.Totals, len(pack.PackBytesB64))
// 3. Hand the pack to the Auditor SDK to verify the
// signature + Merkle structure.
//
// auditor "github.com/.../sdk-auditor-go"
// verdict, err := auditor.VerifyPack(pack)
}
}
Module reference
| Namespace | Methods | Purpose |
|---|---|---|
client.Campaigns | List, Get | Brand-owned campaigns. Read-only. |
client.Proof | Summary, Pack, Chain | Signed evidence (Merkle-rooted per-render ledger). Verify with Auditor SDK. |
client.Metering | Summary, Breakdown | Billable-unit rollups per campaign. |
client.Settlement | Invoices, Invoice, Line | Invoices issued to the brand, with per-line proof-slice roots. |
client.Disputes | Open, List, Get | Brand-raised disputes, backed by signed evidence. The SDK's only write surface. |
See types.go for the full set of typed contracts.
Error model
All non-2xx responses surface as *APIError:
type APIError struct {
Code string // "UNAUTHORIZED", "CAMPAIGN_NOT_FOUND", …
Message string
HTTPStatus int
CorrelationID string // bind to platform logs
Details map[string]any
ServerContractVersion *int
}
Use errors.As to recover:
var apiErr *brand.APIError
if errors.As(err, &apiErr) {
if apiErr.IsRetryable() {
// 408/429/5xx — back-off and retry
}
if apiErr.Code == "CAMPAIGN_NOT_FOUND" { ... }
}
var txErr *brand.TransportError
if errors.As(err, &txErr) {
// DNS / reset / timeout — request never reached the platform.
// Always safe to retry.
}
The SDK does not retry automatically. Brand-side systems typically sit behind their own retry middleware; a second layer of retries causes phantom-duplicate dispute filings. Opt in via apiErr.IsRetryable().
Context and deadlines
Every network method takes a context.Context as its first argument:
ctx, cancel := context.WithDeadline(ctx, deadline)
defer cancel()
campaigns, err := client.Campaigns.List(ctx, brand.CampaignsListOptions{})
The SDK's ClientOptions.Timeout applies if the caller's context has no deadline. Use the caller's context for tight per-call deadlines.
Cross-reference: Auditor SDK
The brand SDK returns evidence; the Auditor SDK verifies it. The two are deliberately separate packages so you can ship the verifier into an air-gapped environment (a regulator's lab, an internal compliance service) without also pulling in the HTTP client.
| Brand SDK output | Auditor SDK function |
|---|---|
SignedProofPack | auditor.VerifyPack(pack) |
ProofRecord + Merkle path | auditor.VerifyLeafInclusion(record, path, root) |
InvoiceLine.ProofSliceRoot | auditor.VerifySlice(slice, root) |
SignedEvidence (dispute) | auditor.VerifyEvidence(evidence, publicKey) |
Versioning
The SDK pins a wire-protocol contract version (ContractVersion). The platform echoes its own version in X-Contract-Version on every response; a brand-side monitoring system that wants to detect drift should compare the two. Minor additions (new optional fields, new metering units) do not bump the contract; non-additive changes do.
Status
Initial release. The brand SDK is API-stable; the underlying platform contracts are at contract version 1.