The Midaz SDK for Go is the idiomatic v3 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. Whether you’re standing up your first ledger or running production payment flows at scale, the SDK keeps you focused on your business logic and out of the boilerplate.Documentation Index
Fetch the complete documentation index at: https://docs.lerian.studio/llms.txt
Use this file to discover all available pages before exploring further.
Getting started
Step 1 – Install Go
Before using the SDK, you must install Go on your machine. v3 declares Go 1.26 ingo.mod. The public API also uses iter.Seq2 and log/slog.
Go to the official Go website.
Step 2 – Create or use an existing Go project
Create a Go project: To create a Go project, use the following command: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 v3 SDK and add it to yourgo.mod and go.sum files:
Step 4 – Import the SDK
Create or open amain.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.
- 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.
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, and every option fails fast at construction time.
Layered design
| Layer | What it handles |
|---|---|
| Client | The main entry point — midaz.New(...) wires authentication, retries, and observability. |
| Services | High-level access to each Midaz domain, exposed as promoted fields on the client. |
| Models | The core data structures that mirror Midaz’s domain logic, re-exported on the midaz package. |
| Utility packages | Modular helpers for config, errors, observability, retries, idempotency, and more. |
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.Services
TheServices 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 initialized and ready to use the moment midaz.New() returns.
Available services
| Service | What it does |
|---|---|
c.Organizations | Manage organizations. |
c.Ledgers | Create and retrieve ledgers. |
c.Assets | Define and manage assets. |
c.AssetRates | Set up and fetch asset exchange rates. |
c.Accounts | Manage accounts and check balances. |
c.AccountTypes | Manage account type definitions. |
c.Portfolios | Group accounts under portfolios. |
c.Segments | Categorize accounts using segments. |
c.Transactions | Create and search financial transactions. |
c.TransactionRoutes | Define and manage transaction routing rules. |
c.Operations | Drill into the atomic operations inside a transaction. |
c.OperationRoutes | Define and manage operation routing rules. |
c.Balances | Get real-time account balances. |
c.Holders | Manage CRM account holders. |
c.Aliases | Manage CRM aliases for accounts and entities. |
c.MetadataIndexes | Manage searchable metadata indexes. |
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 v3, the most common model types are re-exported on themidaz package itself. That means midaz.Account and models.Account are the same type, and most code only needs one import.
Common model types
| Model | What it represents |
|---|---|
midaz.Organization | A business entity that owns ledgers and accounts. |
midaz.Ledger | A collection of accounts and transactions. |
midaz.Asset | A unit of value (currency, token, etc.) that can be stored or moved. |
midaz.Account | An account for tracking assets and balances. |
midaz.Portfolio | A collection of accounts for grouping and management. |
midaz.Segment | A categorization unit for granular organization. |
midaz.Transaction | A financial event composed of multiple operations. |
midaz.Operation | An individual debit or credit entry within a transaction. |
midaz.Balance | The current state of an account’s holdings. |
Utility packages
Inside thepkg folder of the SDK, you’ll find utility packages that target common dev challenges — from config handling to retry policies and security primitives. They split into two groups: core packages that power cross-cutting SDK concerns, and helper packages that provide domain-specific or low-level utilities.
Core packages
| Package | What it solves |
|---|---|
auth | Access Manager OAuth and token lifecycle. Replaces the v2 access-manager package. |
config | Centralized config handling, env overrides, and custom service URLs. |
concurrent | Tools for batching, rate-limiting, and worker pools. |
errors | Structured error types, classifiers, and the canonical Retryable() predicate. |
observability | Tracing, metrics, and logs through one OpenTelemetry provider. |
retry | Retry policy options with exponential backoff and jitter. |
sdkctx | Per-request context flags — idempotency keys, soft vs hard delete, include-deleted. |
validation | Input validation with clear, structured error messages. |
Helper packages
| Package | What it solves |
|---|---|
accounts | Account-specific helpers and convenience functions. |
conversion | Type conversion helpers between models and external formats. |
data | Data utilities and faker helpers for testing and demos. |
format | Utilities for formatting data the Midaz way (dates, times, etc.). |
generator | Demo and mass-data generation for end-to-end scenarios. |
integrity | Checksum and integrity verification utilities. |
performance | Helpers for tuning bulk operations and high-throughput tasks. |
security | Security utilities, including SSRF protection and TLS validation. |
stats | Processing statistics and metrics aggregation. |
transaction | Transaction-building helpers and fluent builders. |
utils | General-purpose helpers used across the SDK. |
version | SDK version metadata and identification. |
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 v3, 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.
Production: Access Manager
Plug your Access Manager credentials intomidaz.WithAccessManager. The SDK eagerly fetches an initial token at construction time, so misconfigurations surface as configuration errors instead of cascading 401s.
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()).Environment loading is explicit in v3 —
config.FromEnvironment() must be in the option chain. The SDK no longer reads env vars implicitly during construction.Multi-tenancy
Tenant scope is derived from the Access Manager / JWT claims used to obtain the token. The SDK applies tenant identity automatically based on 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 v3 ships in three flavors. Pick the one that matches your use case — they’re consistent across every service.
| Method | Returns | Use when |
|---|---|---|
ListXxx | *models.ListResponse[T] (one page) | You want exactly one page and decide when to advance. |
ListXxxAll | iter.Seq2[T, error] | You want every item; the SDK handles paging internally. |
ListXxxPages | iter.Seq2[*ListResponse[T], error] | You need page-level metadata for checkpointing or batching. |
Typed list options
Each list method takes a typed opts struct that embeds one of two base structs depending on how the endpoint paginates:| Pagination shape | Endpoints | Base struct |
|---|---|---|
| Page-based | Organizations, Ledgers, Assets, Portfolios, Segments, Accounts, AccountTypes, Balances, Holders, Aliases | models.PageListOpts{Limit, Page, SortDirection, StartDate, EndDate} |
| Cursor-based | Transactions, Operations, OperationRoutes, TransactionRoutes, AssetRates | models.CursorListOpts{Limit, Cursor, SortDirection, StartDate, EndDate} |
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.
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:
| Field | What it carries |
|---|---|
Category | The error class (validation, authentication, network, configuration, and so on). |
Code | A stable string code suitable for branching or telemetry. |
Operation | The SDK operation that produced the error (e.g. Accounts.GetAccount). |
Resource | The resource family the error refers to, when relevant. |
errors.As, or use the canonical Retryable() method to drive retry policy.
Branch with typed predicates
The SDK ships with a full set ofIs* 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, useClassifyTransportError:
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.
Logging
In v3,
*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.
slog.Handler adapters — the SDK doesn’t care which backend produces the records, only that it speaks slog.
Observability
OpenTelemetry is first-class in v3. 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
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:
Idempotency
Auto-idempotency is on by default in v3. The SDK emits an
X-Idempotency: <uuid> header on every unsafe HTTP request (POST, PUT, PATCH, DELETE), so retries on transient failures don’t accidentally 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:MIDAZ_IDEMPOTENCY=false environment variable when using config.FromEnvironment().
Environment variables
You can configure the SDK using environment variables, no need to hardcode anything. Environment loading is explicit in v3 — pass
config.FromEnvironment() in your config option chain to opt in.
| Variable | Description |
|---|---|
MIDAZ_ENVIRONMENT | Target environment (local, development, production). |
MIDAZ_BASE_URL | Single base URL used when service-specific URLs are not set. |
MIDAZ_ONBOARDING_URL | Override the onboarding service base URL. |
MIDAZ_TRANSACTION_URL | Override the transaction service base URL. |
MIDAZ_CRM_URL | Override the CRM service base URL (used by Holders and Aliases). |
PLUGIN_AUTH_ENABLED | Enable Access Manager authentication (true or false). |
PLUGIN_AUTH_ADDRESS | Address of the Access Manager service. |
MIDAZ_CLIENT_ID | Client ID for Access Manager authentication. |
MIDAZ_CLIENT_SECRET | Client secret for Access Manager authentication. |
MIDAZ_TIMEOUT | HTTP request timeout in seconds. |
MIDAZ_USER_AGENT | Custom User-Agent header sent with every request. |
MIDAZ_DEBUG | Enable debug logs (true or false). |
MIDAZ_MAX_RETRIES | Maximum retry attempts for failed requests. |
MIDAZ_IDEMPOTENCY | Enable automatic idempotency key generation for unsafe requests. |
MIDAZ_SKIP_AUTH_CHECK | Skip Access Manager config validation at startup (testing only). |
midaz, pkg/config, and pkg/sdkctx, see the Configuration guide in the SDK repo.
Example projects
| Example | What it demonstrates |
|---|---|
01-hello-world | Minimal init plus the first API call (~17 body lines). |
02-auth | Access Manager authentication (production setup). |
03-end-to-end | Org → ledger → asset → account → transaction. |
04-listing-cursor | Cursor-based pagination with iter.Seq2. |
05-listing-pages | Page-based pagination — List / ListAll / ListPages. |
06-idempotency | Auto / explicit / suppressed idempotency modes. |
07-retries | Default policy, custom policy, disabled retries. |
08-logging-slog | *slog.Logger integration (v3 logging). |
09-testing-with-mocks | go.uber.org/mock for unit testing your code against the SDK. |
10-observability-otel | Full OpenTelemetry surface (tracing + metrics + logs). |
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
v3 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 /v3 and walk the breaking changes with the side-by-side examples in the migration guide.
The biggest moves to plan for:
- Module path and package name:
github.com/LerianStudio/midaz-sdk-golang/v3(was/v2), packagemidaz(wasclient). - Authentication:
WithAuthTokenis gone. UseWithAccessManagerfor production orWithAnonymousfor local stacks. One auth source is required at construction time. - Service access:
c.Accounts.X(wasc.Entity.Accounts.X). Thec.Entityfield still exists for compatibility, but every example uses the short form. - Pagination:
models.ListOptionsand its 30 fluent setters are gone. Use the typed per-endpoint opts (models.AccountsListOpts,models.TransactionsListOpts, …) and the newListAll/ListPagesiterators. - Errors:
*MidazErroris gone. Service-layer errors now use*pkg/errors.ErrorwithRetryable()as the official retry source. Local validation may surface*pkg/validation.FieldErrors. - Tenant identity:
WithTenantID, theMIDAZ_TENANT_IDenv var, and theX-Tenant-IDheader are gone. Tenant scope flows through Access Manager / JWT claims. - Logging:
*slog.Loggerreplaces the bespokeobservability.Loggerinterface as the canonical surface. The SDK is silent by default.
Explore the APIs
For more information about the APIs, refer to the following links:

