Skip to main content
The Midaz SDK for Go is the idiomatic v4 client for the Midaz financial-ledger APIs. It gives you typed access to every service — Organizations, Ledgers, Accounts, Transactions, and more — with a single surface for authentication, pagination, errors, logging, and observability. It handles the boilerplate so your code stays focused on business logic.
Coming from v2? v4 is a clean major version with breaking changes across authentication, pagination, errors, and service access. There is no deprecation window — you swap your import from /v2 to /v4 and migrate at the same time.See Migrating from v2 before you upgrade.

Getting started


Step 1 – Install Go

Before using the SDK, you must install Go on your machine. v4 declares Go 1.26 in go.mod. The public API also uses iter.Seq2 and log/slog.
2
Download the installer for your OS (Windows, macOS, or Linux).

Step 2 – Create or use an existing Go project

Create a Go project: To create a Go project, use the following command:
Use an existing Go project: If you’re working in an existing project, make sure there’s a go.mod file in the root. If not, run the following command to create one:

Step 3 – Add the Midaz SDK

Inside your project directory, run the following command to pull the v4 SDK and add it to your go.mod and go.sum files:
The module path requires the /v4 suffix. If you omit it, Go will resolve a stale pre-v4 release that is missing every change shipped in this version. Always import github.com/LerianStudio/midaz-sdk-golang/v4.
Using VS Code or GoLand? Your IDE may automatically run go get when you import a new package.

Step 4 – Import the SDK

Create or open a main.go file and add the following content. The example below builds a client against your local Midaz stack with anonymous authentication, lists organizations, then creates a new one.
The example below targets a local Midaz stack with auth disabled. If you don’t have one running yet, see Getting started with Midaz to spin one up before running the snippet.
This gives you access to:
  • The Midaz client for calling every API service.
  • Built-in data models (like CreateOrganizationInput).
  • Auth via Access Manager (production) or Anonymous (local development).
  • A typed configuration system that fails fast at construction time.
Want to learn more about authentication? Jump to the Authentication section for the full setup.

Step 5 – Run the project

Run the following command:

SDK architecture


The Midaz SDK for Go is built around clarity and predictability. Every service is reachable directly on the client. Every list method follows the same trio shape. Every error is structured. Every option fails fast at construction time.

Layered design

Services are reached directly on the client — c.Accounts, c.Transactions, c.Organizations. You may still see an embedded Entity field in autocomplete, but new code should use the direct service fields shown in this guide.
Want to dive deeper? Check the architecture guide for the full story behind the rewrite.

Services

The Services layer is your access point to every Midaz domain. Each service handles one resource family and ships every method as part of its single interface — no UseAllAPIs() toggle, no service registration step. Every service is ready to use the moment midaz.New() returns.

Available services

Models

Models reflect how Midaz thinks about finance, with each type tied closely to a real-world business concept. You’ll use them across every service call — from onboarding accounts to recording multi-leg transactions. In v4, the most common model types are re-exported on the midaz package itself. That means midaz.Account and models.Account are the same type, and most code only needs one import.

Common model types

Need a builder, an internal request shape, or a deprecated type? Import github.com/LerianStudio/midaz-sdk-golang/v4/models directly — every type lives there, and the midaz aliases preserve type identity, so the two import paths interoperate cleanly.

Utility packages

Inside the pkg folder of the SDK, you’ll find utility packages for common dev challenges — config handling, retry policies, and security primitives. They split into two groups. Core packages power cross-cutting SDK concerns. Helper packages provide domain-specific or low-level utilities.

Core packages

Helper packages

The v2 pkg/access-manager package has moved to pkg/auth (so the directory matches the package name). The v2 pkg/pagination package was removed — its surface lives in models and on each service today.

Authentication


In v4, the SDK requires exactly one authentication source at construction time. Calling midaz.New(...) with neither returns a typed configuration error — no more silent 401 cascades on the first API call. You have two choices:
  • midaz.WithAccessManager(...) — production-shape OAuth via the Lerian Access Manager. Recommended for any non-local stack.
  • midaz.WithAnonymous() — opt out of authentication entirely. Suitable only for a local Midaz stack with auth disabled.
The two options are mutually exclusive.

Production: Access Manager

Plug your Access Manager credentials into midaz.WithAccessManager. The SDK eagerly fetches an initial token at construction time, so misconfigurations surface as configuration errors instead of cascading 401s.
Replace the values in the // Configure Access Manager block with your own credentials before running.
The SDK requests a token from your Access Manager, attaches it to every API call, and refreshes it automatically when it expires.

Local development: Anonymous

For a local Midaz stack with auth disabled, opt out explicitly:

Configure via environment variables

You can also point the SDK at your Access Manager via environment variables. Export them in your shell or your process manager:
config.FromEnvironment() reads the process environment, not a .env file. If you keep variables in a .env file during development, load them with a library like godotenv before calling config.NewConfig(config.FromEnvironment()).
Then opt in to environment loading at config time:
Environment loading is explicit in v4 — config.FromEnvironment() must be in the option chain. The SDK no longer reads env vars implicitly during construction.
Want the full auth walkthrough? Check the Authentication guide in the SDK repo.

Multi-tenancy


Tenant scope comes from the Access Manager / JWT claims used to obtain the token. The SDK applies tenant identity automatically from those claims — no extra configuration on the client side. To run calls under a different tenant scope, use a separate set of Access Manager credentials — or build a second client with its own token context.

Listing and iteration


Every list endpoint in v4 ships in three flavors. Pick the one that matches your use case — they’re consistent across every service.

Typed list options

Each list method takes a typed opts struct that embeds one of two base structs depending on how the endpoint paginates: Each endpoint also exposes a typed Filters sub-struct with only the fields that endpoint actually honors. Setting a field on the wrong shape — for example, Page on a cursor endpoint — fails at compile time, not silently at runtime.

Iterate every item with ListAll

The most idiomatic shape: a range loop over every item across every page. The SDK advances cursors and fetches pages internally.

Iterate page envelopes with ListPages

When you need page-level metadata — for checkpointing, batching, or stopping mid-page — iterate over ListXxxPages instead. Every entry is a *ListResponse[T] with the full Pagination block attached.
See the Pagination guide in the SDK repo for HasMore() semantics, NextCursor handling, and the page-vs-cursor decision table.

Error handling


Most errors returned by the SDK service layer are *pkg/errors.Error with structured fields: You can branch with typed predicates, walk fields with errors.As, or use the canonical Retryable() method to drive retry policy.

Branch with typed predicates

The SDK ships with a full set of Is* predicates so you can match errors without poking at internals:

Drive retry decisions with Retryable()

The Error.Retryable() method is the canonical retry-policy source. Use it instead of building your own classification.

Wrap raw transport errors

If you’re calling lower-level HTTP code outside the SDK and want the same structured error shape, use ClassifyTransportError:

Local validation: FieldErrors

Local input validation surfaces *pkg/validation.FieldErrors — a structured collection of per-field complaints from the SDK before any HTTP call. Use errors.As to inspect them when validating user input or builders.
Want to dive deeper? Check the Error handling guide in the SDK repo for the full category map, every code, and retry boundary semantics.

Logging


In v4, *slog.Logger is the canonical logger surface. Wire it via midaz.WithLogger(...). The SDK is silent by default — it uses slog.DiscardHandler until you opt in. That means no surprise log lines in your stdout, and no fighting with the SDK over log format. You decide the handler, the level, and the destination.
Need zap, zerolog, or charmbracelet/log? They all integrate as slog.Handler adapters — the SDK doesn’t care which backend produces the records, only that it speaks slog.
Wiring zap, zerolog, or another backend? The Logging guide has adapter recipes for every common logging library.

Observability


OpenTelemetry is first-class in v4. One observability provider gives you spans, metrics, and OTel-correlated logs through a single wiring point. Configure observability either by passing a fully-built provider, or by passing options that the SDK assembles for you.

Wire a provider

Or pass options inline

The SDK emits one HTTP span per outbound request with proper W3C traceparent propagation. Business log records carry safe IDs only — never payloads, names, addresses, or auth headers. You can also wrap a block of business logic in a span via Client.Trace:
WithObservabilityOptions and WithObservabilityProvider use replacement semantics, not merge. Each call replaces any previously installed provider. To start from a baseline, include observability.WithDevelopmentDefaults or observability.WithProductionDefaults as the first option in the chain.
Want to dive deeper? See the 10-observability-otel example in the SDK repo for span attributes, metric names, and exporter setup.

Idempotency


Auto-idempotency is on by default in v4. The SDK emits an X-Idempotency: <uuid> header on every unsafe HTTP request (POST, PUT, PATCH, DELETE). Retries on transient failures then do not double-create resources. You can override the auto-generated key per-call when you need a stable, caller-supplied key — typical for saga steps, outbox rows, or UI-driven submissions.

Set a stable key per request

Suppress idempotency for one call

For rare fire-and-forget administrative endpoints, suppress the header per-request:

Disable globally

If you don’t want auto-idempotency anywhere, turn it off at the client level:
You can also disable it via the MIDAZ_IDEMPOTENCY=false environment variable when using config.FromEnvironment().

Environment variables


You can configure the SDK with environment variables instead of hardcoded values. Environment loading is explicit in v4 — pass config.FromEnvironment() in your config option chain to opt in. For the full option matrix across midaz, pkg/config, and pkg/sdkctx, see the Configuration guide in the SDK repo.

Example projects


The SDK ships with a numbered tour of its core capabilities (examples 01–10) plus a set of advanced and reference examples. The numbered set is focused tutorials: each one teaches exactly one concept with the smallest possible body. Browse them in the examples directory on GitHub.
The repository also includes specialized and reference examples — concurrency, configuration, context, tracing, tracing-server, pkg-validation-demo, mass-demo-generator, and workflow-with-entities. They cover advanced patterns (bounded parallelism, OTel context propagation across processes, mass data generation) for when you’ve outgrown the focused set.

Migrating from v2


v4 is a clean-cut major version with no deprecation window. There is no transitional release, no // Deprecated: shim, no backward-compatible alias of the old surface. Swap your import from /v2 to /v4 and walk the breaking changes below. The biggest moves to plan for:
  • Module path and package name: github.com/LerianStudio/midaz-sdk-golang/v4 (was /v2), package midaz (was client).
  • Authentication: WithAuthToken is gone. Use WithAccessManager for production or WithAnonymous for local stacks. One auth source is required at construction time.
  • Service access: c.Accounts.X (was c.Entity.Accounts.X). The c.Entity field still exists for compatibility, but every example uses the short form.
  • Pagination: models.ListOptions and its 30 fluent setters are gone. Use the typed per-endpoint opts (models.AccountsListOpts, models.TransactionsListOpts, …) and the new ListAll / ListPages iterators.
  • Errors: *MidazError is gone. Service-layer errors now use *pkg/errors.Error with Retryable() as the official retry source. Local validation may surface *pkg/validation.FieldErrors.
  • Tenant identity: WithTenantID, the MIDAZ_TENANT_ID env var, and the X-Tenant-ID header are gone. Tenant scope flows through Access Manager / JWT claims.
  • Logging: *slog.Logger replaces the bespoke observability.Logger interface as the canonical surface. The SDK is silent by default.
For the authentication changes in detail, see the Authentication guide in the SDK repo. The examples directory shows the v4 API in practice.

Explore the APIs


For more information about the APIs, refer to the following links:

External API mapping

Internal API mapping

Godoc documentation