The Midaz Go SDK is the idiomatic v3 client for the Midaz financial-ledger
APIs. v3 is a clean major version: typed list-opts, iter.Seq2-based
pagination, structured errors with retry classification, *slog.Logger
canonical logging, OpenTelemetry observability, and a single canonical
auth surface (Access Manager OAuth or anonymous local-stack mode).
v3 is the result of a 9-track DX overhaul. Highlights:
- One auth source, enforced:
WithAccessManagerfor production OAuth,WithAnonymousfor local stacks. CallingNew()with neither returns a typed configuration error at construction time. - Typed pagination opts at the type system: page-based and cursor-based endpoints have separate opts types. Wrong-shape opts don't compile.
iter.Seq2[T, error]: every list method ships in a trio —List(one page) /ListAll(every item) /ListPages(every page envelope).- Structured errors: every error is a
*pkg/errors.ErrorwithCategory,Code,Operation,Resource, and a canonicalRetryable()method. Real network/timeout/auth/validation classification. - Canonical logging:
*slog.Loggeris the canonical client/application logger surface. The SDK is silent by default (slog.DiscardHandler); opt in withWithLogger. A separateProvider.Logger(OTel-correlated) is also exposed bypkg/observabilityfor span-aware logging from inside SDK callbacks. - OpenTelemetry first-class: spans + metrics + logs through one
observability.Providerwired byWithObservabilityProvider. - Idempotency by default: auto-generated
X-Idempotencyper unsafe request. Override withsdkctx.WithIdempotencyKeyfor stable caller-supplied keys; suppress per-call withWithoutAutoIdempotency. - Mocks via
go.uber.org/mock: pre-generated mocks for every service ship underentities/mocks/. Regenerate withgo generate ./entities/....
Historical planning artifact — see docs/v3-dx-plan.md for the original design rationale (note: file:line refs may be stale).
go get github.com/LerianStudio/midaz-sdk-golang/v3Requires Go 1.26+ — the toolchain pinned in go.mod. The SDK uses
iter.Seq2 (Go 1.23+) and log/slog (Go 1.21+) in its public API; the
1.26 floor matches the rest of the Lerian Go stack.
The minimum-viable shape — local stack, anonymous auth, list 5 organizations:
package main
import (
"context"
"fmt"
"log"
midaz "github.com/LerianStudio/midaz-sdk-golang/v3"
"github.com/LerianStudio/midaz-sdk-golang/v3/models"
)
func main() {
c, err := midaz.New(
midaz.WithEnvironment(midaz.EnvironmentLocal),
midaz.WithAnonymous(),
)
if err != nil {
log.Fatalf("midaz.New: %v", err)
}
defer c.Shutdown(context.Background())
page, err := c.Organizations.ListOrganizations(context.Background(),
models.OrganizationsListOpts{
PageListOpts: models.PageListOpts{Limit: 5},
})
if err != nil {
log.Fatalf("ListOrganizations: %v", err)
}
for _, org := range page.Items {
fmt.Printf("- %s (%s)\n", org.LegalName, org.ID)
}
}For Access Manager auth (production) and the full client-construction
matrix see docs/auth.md and docs/configuration.md.
Every public service is a promoted field on *midaz.Client. The canonical
shape is c.<Service>.<Method>:
orgs, err := c.Organizations.ListOrganizations(ctx, opts)
ledger, err := c.Ledgers.CreateLedger(ctx, orgID, input)
account, err := c.Accounts.GetAccount(ctx, orgID, ledgerID, accountID)
balance, err := c.Balances.GetBalance(ctx, orgID, ledgerID, balanceID)
tx, err := c.Transactions.CreateTransaction(ctx, orgID, ledgerID, input)The full service list: Organizations, Ledgers, Assets, AssetRates,
Accounts, AccountTypes, Balances, Holders, MetadataIndexes,
Operations, OperationRoutes, Portfolios, Segments, Transactions,
TransactionRoutes.
Every list method ships in three flavors:
// One page, you decide when to advance.
page, err := c.Accounts.ListAccounts(ctx, orgID, ledgerID, opts)
// iter.Seq2 over every item across every page (SDK handles paging).
for acc, err := range c.Accounts.ListAccountsAll(ctx, orgID, ledgerID, opts) {
if err != nil { return err }
process(acc)
}
// iter.Seq2 over page envelopes (with metadata for checkpointing).
for batch, err := range c.Accounts.ListAccountsPages(ctx, orgID, ledgerID, opts) {
if err != nil { return err }
log.Printf("page %d: %d items", batch.Pagination.Page, len(batch.Items))
}Page-based and cursor-based endpoints use separate opts types. See
examples/05-listing-pages/ and
examples/04-listing-cursor/.
Auto-on by default. The SDK emits X-Idempotency: <uuid> on every unsafe
request. Override per-call:
import "github.com/LerianStudio/midaz-sdk-golang/v3/pkg/sdkctx"
// Stable key for at-least-once producers (saga steps, outbox rows, UI submissions):
ctx := sdkctx.WithIdempotencyKey(ctx, "tx-2026-05-06-001")
// Suppress for one call (rare — fire-and-forget administrative endpoints):
ctx := sdkctx.WithoutAutoIdempotency(ctx)Disable globally with midaz.WithIdempotency(false). See
examples/06-idempotency/.
Every error is a *pkg/errors.Error. Use the typed predicates or
errors.As for structured field access:
import sdkerrors "github.com/LerianStudio/midaz-sdk-golang/v3/pkg/errors"
acc, err := c.Accounts.GetAccount(ctx, orgID, ledgerID, accountID)
if err != nil {
switch {
case sdkerrors.IsNotFoundError(err):
return fmt.Errorf("account not found: %w", err)
case sdkerrors.IsAuthError(err):
return fmt.Errorf("re-authenticate: %w", err)
case sdkerrors.IsValidationError(err):
return fmt.Errorf("input invalid: %w", err)
case sdkerrors.IsNetworkError(err):
return fmt.Errorf("transient transport: %w", err) // retry-safe
}
}Or walk fields:
var sdkErr *sdkerrors.Error
if errors.As(err, &sdkErr) {
log.Printf("op=%s resource=%s code=%s retryable=%v",
sdkErr.Operation, sdkErr.Resource, sdkErr.Code, sdkErr.Retryable())
}Retryable() is the canonical retry-policy source — derived from
Category. Use it instead of re-implementing a category switch in
consumer code.
Inject a *slog.Logger:
import "log/slog"
logger := slog.New(slog.NewJSONHandler(os.Stdout, nil))
c, err := midaz.New(
midaz.WithEnvironment(midaz.EnvironmentLocal),
midaz.WithAnonymous(),
midaz.WithLogger(logger),
)Adapters for zap, zerolog, logrus all go through slog.Handler. See
docs/logging.md and examples/08-logging-slog/.
Default policy: 3 retries, exponential backoff with 25% jitter, retryable on transport errors + 5xx + 408 + 425 + 429. Customize:
Unsafe methods (POST, PUT, PATCH, DELETE) retry only when
X-Idempotency is present. The SDK auto-generates this header by default;
WithoutAutoIdempotency or WithIdempotency(false) disables automatic unsafe
retries unless the caller supplies X-Idempotency explicitly.
import "github.com/LerianStudio/midaz-sdk-golang/v3/pkg/retry"
c, err := midaz.New(
midaz.WithEnvironment(midaz.EnvironmentLocal),
midaz.WithAnonymous(),
midaz.WithRetryOptions(
retry.WithMaxRetries(5),
retry.WithInitialDelay(200*time.Millisecond),
),
)Or WithCustomRetryPolicy(func(*Response, error) bool) for arbitrary
predicates. Disable with WithoutRetries(). See examples/07-retries/.
import "github.com/LerianStudio/midaz-sdk-golang/v3/pkg/observability"
provider, err := observability.New(ctx,
observability.WithServiceName("payments-api"),
observability.WithEnvironment("production"),
observability.WithComponentEnabled(true, true, true), // tracing, metrics, logs
)
if err != nil { return err }
defer provider.Shutdown(ctx)
c, err := midaz.New(
midaz.WithEnvironment(midaz.EnvironmentProduction),
midaz.WithAccessManager(am),
midaz.WithObservabilityProvider(provider),
)The SDK emits one HTTP span per outbound request with proper W3C
traceparent propagation. Business logs carry safe IDs only — never
payloads, names, addresses, or auth headers. See docs/tracing.md
and examples/10-observability-otel/.
c, err := midaz.New(
midaz.WithEnvironment(midaz.EnvironmentProduction),
midaz.WithAccessManager(am),
)Tenant scope is derived from the Access Manager/JWT claims used to obtain the
token. The SDK does not expose tenant configuration and does not send
X-Tenant-ID; use separate Access Manager credentials/token context when tenant
scope differs.
Every service has a generated mock under entities/mocks/:
import (
"github.com/LerianStudio/midaz-sdk-golang/v3/entities/mocks"
"go.uber.org/mock/gomock"
)
func TestMyHandler(t *testing.T) {
ctrl := gomock.NewController(t)
mockSvc := mocks.NewMockAccountsService(ctrl)
mockSvc.EXPECT().
GetAccount(gomock.Any(), "org-1", "ledger-1", "acc-1").
Return(&models.Account{ID: "acc-1"}, nil)
// ... use mockSvc as entities.AccountsService in your code under test
}Mocks are regenerated via go generate ./entities/... (each service
file has a //go:generate mockgen directive). See examples/09-testing-with-mocks/.
The SDK reads these only when config.FromEnvironment() is in the option
chain — there is no implicit env-var loading:
- Service URLs:
MIDAZ_ENVIRONMENT,MIDAZ_BASE_URL,MIDAZ_ONBOARDING_URL,MIDAZ_TRANSACTION_URL,MIDAZ_CRM_URL - Auth:
PLUGIN_AUTH_ENABLED,PLUGIN_AUTH_ADDRESS,MIDAZ_CLIENT_ID,MIDAZ_CLIENT_SECRET - Behavior:
MIDAZ_TIMEOUT,MIDAZ_USER_AGENT,MIDAZ_DEBUG,MIDAZ_MAX_RETRIES,MIDAZ_IDEMPOTENCY,MIDAZ_ERROR_EXPOSE_BODY
See docs/configuration.md for the full matrix.
docs/auth.md— authentication setup and migrationdocs/configuration.md— every available SDK option, both layersdocs/multi-tenancy.md— tenant routingdocs/logging.md—*slog.Loggercontract + adapter recipesdocs/tracing.md— OpenTelemetry tracing + metrics + business logsdocs/pagination.md— pagination contractdocs/errors.md— error categories, codes, retry boundariesdocs/examples.md— runnable example indexdocs/v3-dx-plan.md— historical v3 planning artifact (file:line refs may be stale)pkg.go.dev/github.com/LerianStudio/midaz-sdk-golang/v3— generated API reference
Generate docs locally:
make docs # static HTML to docs/godoc/
make godoc # interactive server at http://localhost:6060See examples/README.md for the full numbered list
with a Start-Here / Common workflows / Behavior & resilience / Testing &
observability / Reference structure. Highlights:
examples/01-hello-world/— minimum-viable shape (~17 body lines)examples/02-auth/— Access Manager authenticationexamples/03-end-to-end/— org → ledger → account → transactionexamples/06-idempotency/— idempotency mode referenceexamples/07-retries/— retry policiesexamples/08-logging-slog/—*slog.Loggerintegrationexamples/09-testing-with-mocks/— unit-testing patternexamples/10-observability-otel/— OpenTelemetryexamples/mass-demo-generator/— production-shaped data generator at scaleexamples/workflow-with-entities/— every public service through full CRUD
Run the demo data generator:
make demo-data
# or
DEMO_NON_INTERACTIVE=1 go run ./examples/mass-demo-generator --org-locale=brBuild all examples:
make examples-testmake test # unit tests
make coverage # HTML coverage report under artifacts/
make verify-sdk # API compatibility + parity checks
make examples-test # build every example, run example tests
make ci # full local pipeline: tidy + fmt + lint + gosec + test + verify-sdkContributions are welcome. See CONTRIBUTING.md for guidelines.
This project is licensed under the Apache License, Version 2.0. See LICENSE.md for details.
Copyright 2025 Lerian Studio
