EnfinitOSEnfinitOS
DevelopersOperator & brand
Production-ready scaffold

Brand SDK — Java

Maven-published JDK 17+ client. Drop into Spring Boot or any JVM stack the buy-side already runs.

com.enfinitos:sdk-brandSubstrate AllJava
Install

Get the SDK

<dependency>
  <groupId>com.enfinitos</groupId>
  <artifactId>sdk-brand</artifactId>
  <version>0.0.1</version>
</dependency>

About this status badge

Typed, tested, documented, and wired to the EnfinitOS platform endpoints that exist today. Vendor-side SDK integrations (Broadsign / VIOOH / DJI / Tizen / Alexa / Twilio / Stripe / etc.) land per-customer at pilot integration time — those bring the renderer/transport/exchange-specific code; the EnfinitOS half is ready.

README

The developer-facing documentation in full

The same README the SDK package ships with — rendered here at build time so what you read matches exactly what you install.

EnfinitOS Brand SDK — Java

com.enfinitos:sdk-brand — the read-only REST client a brand (advertiser) uses to query its own delivery proof, metering, and settlement records directly from the EnfinitOS platform, without going through the operator's reporting plane.

Mirrors @enfinitos/sdk-brand (TypeScript) and enfinitos_brand (Python) one-for-one. When one moves, the other moves.

Who should use it

A brand engineering team that wants to:

  • pull a Merkle-rooted signed proof of every billable delivery, and verify it against the Auditor SDK;
  • reconcile its own attribution numbers against the platform's metered usage before paying the invoice;
  • iterate settlement invoices and lines for finance/AP;
  • open a dispute (with signed counter-evidence) when the brand's own auditor disagrees with what was billed.

The SDK is scoped read-only to campaigns the calling brand owns. The single write operation — disputes.open — is bound to the brand's auditor key and is itself idempotent.

Authentication

Every request carries:

  • Authorization: Bearer <brand_api_key> — issued by the platform to a single brand tenant; rotateable; read-only on owned campaigns plus dispute-open;
  • X-Enfinitos-Brand: <brand_id> — the brand's tenant id; allows the platform WAF to rate-limit per-tenant before auth decode.

The platform rejects any mismatch between the key's owner and the header.

Installation

This SDK is currently distributed as part of the EnfinitOS monorepo. Add as a Maven dependency:

<dependency>
  <groupId>com.enfinitos</groupId>
  <artifactId>sdk-brand</artifactId>
  <version>0.0.1</version>
</dependency>

Requires Java 17+. Zero third-party runtime dependencies.

Getting started

import com.enfinitos.brand.EnfinitOSBrandClient;
import com.enfinitos.brand.CampaignsApi;
import com.enfinitos.brand.ProofApi;

var client = EnfinitOSBrandClient.builder()
    .apiBaseUrl("https://api.enfinitos.com")
    .brandId("brand_acme_co")
    .apiKey(System.getenv("ENFINITOS_BRAND_API_KEY"))
    .build();

// 1. List my campaigns.
var campaigns = client.campaigns().list(CampaignsApi.ListOptions.defaults());
for (var c : campaigns.items()) {
    System.out.printf("%s [%s] billed=%s%n",
        c.campaignId(), c.status(), c.totalBilledMinor());
}

// 2. Fetch a signed proof pack for one campaign.
var pack = client.proof().pack(campaigns.items().get(0).campaignId());

// 3. Verify against the Auditor SDK (separate artifact —
//    com.enfinitos:sdk-auditor — performs Merkle + signature
//    verification offline, with no network round-trip).
//
//    var ok = new AuditorClient(...).verifyPack(pack);
//    if (!ok.valid()) throw new IllegalStateException("proof failed");

Module reference

client.campaigns()

  • list(ListOptions) — cursor-paginated list of owned campaigns.
  • get(campaignId) — single campaign.

ListOptions accepts (status, cursor, limit). null defaults are fine.

client.proof()

  • summary(campaignId) — cheap Merkle-root rollup (merkleRoot, recordCount, signer info).
  • pack(campaignId) — full signed SignedProofPack; pass to the Auditor SDK for offline verification.
  • chain(campaignId, ChainOptions) — cursor-paginated per-leaf records.

For verification use pack — the chain endpoint is for inspection and incremental reconciliation only.

client.metering()

  • summary(campaignId) — per-unit totals.
  • breakdown(campaignId, MeteringUnit) — per-day, per-substrate rollup of a single billable unit.

client.settlement()

  • invoices(InvoicesOptions) — invoices issued to the brand; optional (from, to) window on issuedAt.
  • invoice(invoiceId) — single invoice with lines.
  • line(lineId) — single invoice line; carries the proofSliceRoot that pins it to the corresponding Merkle subtree.

client.disputes()

  • open(OpenInput) — open a dispute. The SDK auto-generates an idempotency key when one isn't supplied; cron re-runs are safe.
  • list(ListOptions) — list the brand's disputes.
  • get(disputeId) — single dispute.

The dispute body's free-form reason is informational; the operator's response is bound to the SignedEvidence only.

Error model

The SDK raises two exception families:

  • EnfinitOSApiException — the platform answered with a non-2xx status or a 2xx envelope carrying ok: false. Carries code (stable string identifier), httpStatus, correlationId, optional details, and an isRetryable() helper.
  • EnfinitOSTransportException — connection-level failure (DNS, timeout, TLS, refused).

Typical pattern:

try {
    client.campaigns().get(id);
} catch (EnfinitOSApiException e) {
    if (e.isRetryable()) {
        // 408 / 429 / 5xx — defer to your scheduler's backoff.
    } else if ("CAMPAIGN_NOT_FOUND".equals(e.code())) {
        // tenant-bound 404 — the campaign either doesn't exist
        // or isn't owned by this brand. The platform deliberately
        // does NOT distinguish the two.
    } else {
        throw e;
    }
}

The SDK does NOT retry by default. Brand-side systems sit downstream of the brand's own retry middleware (Sidekiq, JVM schedulers, Spring Retry, ...); doubling them up has caused duplicate-dispute filings in the past. Opt in by checking isRetryable() and re-issuing yourself.

Cross-reference

  • Auditor SDK (com.enfinitos:sdk-auditor) — offline verification of signed proof packs returned by proof.pack. Pure crypto; no network.
  • Operator Reporting — the platform's operator-facing report surface answers the same questions from the operator's point of view; brands deliberately do NOT consume it directly.
  • REST contractapps/api is the source of truth; the SDK pins shapes via record types and is regenerated when contracts move.

Wire conventions

Every request also includes:

  • X-Enfinitos-Sdk: brand-java
  • X-Enfinitos-Sdk-Version: 0.0.1
  • X-Enfinitos-Contract: 1
  • User-Agent: enfinitos-sdk-brand-java/0.0.1 [(tag)]
  • Idempotency-Key: <uuid> on POST /v1/brand/disputes (auto-generated by the SDK when the caller omits one).

The platform's response envelope is:

{ "ok": true, "data": { ... }, "contractVersion": 1 }

or

{ "ok": false, "error": { "code": "...", "message": "...", "correlationId": "..." } }

The SDK unwraps both and surfaces only data or the typed EnfinitOSApiException. The X-Contract-Version response header is captured on errors for drift-monitoring tooling.

Tests

mvn -q test

Tests use the injectable HttpTransport.Doer interface; no real HTTP server is required.

API reference

Hit the HTTP surface directly

The Brand SDK — Java is a thin client over the same governed HTTP API every other SDK calls. The full OpenAPI 3.1 reference lives on the docs site, published alongside the April 2027 platform launch.

Sandbox

Run this SDK against a real tenant

The browser demo at enfinitos.com/sandbox runs today against a shared synthetic tenant. The dedicated developer sandbox — your own persistent tenant, API keys, full HTTP-contract coverage — opens ahead of the April 2027 platform launch.