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 grounded in the 2026 platform reality. Awaiting first customer-integration validation.

README

The developer-facing documentation in full

Rendered from packages/sdks/brand-java/README.md at build time — the same source the package ships with.

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.

Sandbox

Run this SDK against a real tenant

The hosted sandbox is the fastest way to verify Brand SDK — Java against a real EnfinitOS tenant before committing to a pilot. Launching Q4 2026.