Skip to main content

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.

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.
Coming from v2? v3 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 /v3 and migrate at the same time.Read the Migration guide from v2 to v3 before upgrading.

Getting started


Step 1 – Install Go

Before using the SDK, you must install Go on your machine. v3 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:
mkdir my-midaz-app
cd my-midaz-app
go mod init my-midaz-app
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:
go mod init your-module-name

Step 3 – Add the Midaz SDK

Inside your project directory, run the following command to pull the v3 SDK and add it to your go.mod and go.sum files:
The module path requires the /v3 suffix. If you omit it, Go will resolve a stale pre-v3 release that is missing every change shipped in this version. Always import github.com/LerianStudio/midaz-sdk-golang/v3.
go get github.com/LerianStudio/midaz-sdk-golang/v3
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.
package main

import (
	"context"
	"fmt"
	"log"

	"github.com/LerianStudio/midaz-sdk-golang/v3"
	"github.com/LerianStudio/midaz-sdk-golang/v3/models"
)

func main() {
	// Build a client. v3 requires exactly one auth source — use
	// midaz.WithAnonymous() for a local stack, or midaz.WithAccessManager(...)
	// for a development or production environment.
	c, err := midaz.New(
		midaz.WithEnvironment(midaz.EnvironmentLocal),
		midaz.WithAnonymous(),
	)
	if err != nil {
		log.Fatalf("midaz.New: %v", err)
	}
	defer c.Shutdown(context.Background())

	ctx := context.Background()

	// List the first 5 organizations using a typed list-opts struct.
	page, err := c.Organizations.ListOrganizations(ctx, 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)
	}

	// Create a new organization. Notice that midaz.CreateOrganizationInput is
	// the same type as models.CreateOrganizationInput — re-exported on the
	// midaz package so most user code only needs one import.
	dba := "Example Inc."
	org, err := c.Organizations.CreateOrganization(ctx, &midaz.CreateOrganizationInput{
		LegalName:       "Example Corporation",
		LegalDocument:   "123456789",
		DoingBusinessAs: &dba, // optional fields are *string — use &local for short literals
		Address: midaz.Address{
			Line1:   "123 Main St",
			City:    "New York",
			State:   "NY",
			ZipCode: "10001",
			Country: "US",
		},
	})
	if err != nil {
		log.Fatalf("CreateOrganization: %v", err)
	}

	fmt.Printf("Organization created: %s\n", org.ID)
}
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:
go run main.go

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

LayerWhat it handles
ClientThe main entry point — midaz.New(...) wires authentication, retries, and observability.
ServicesHigh-level access to each Midaz domain, exposed as promoted fields on the client.
ModelsThe core data structures that mirror Midaz’s domain logic, re-exported on the midaz package.
Utility packagesModular 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.
Want to dive deeper? Check the v3 design rationale for the full architectural 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 initialized and ready to use the moment midaz.New() returns.

Available services

ServiceWhat it does
c.OrganizationsManage organizations.
c.LedgersCreate and retrieve ledgers.
c.AssetsDefine and manage assets.
c.AssetRatesSet up and fetch asset exchange rates.
c.AccountsManage accounts and check balances.
c.AccountTypesManage account type definitions.
c.PortfoliosGroup accounts under portfolios.
c.SegmentsCategorize accounts using segments.
c.TransactionsCreate and search financial transactions.
c.TransactionRoutesDefine and manage transaction routing rules.
c.OperationsDrill into the atomic operations inside a transaction.
c.OperationRoutesDefine and manage operation routing rules.
c.BalancesGet real-time account balances.
c.HoldersManage CRM account holders.
c.AliasesManage CRM aliases for accounts and entities.
c.MetadataIndexesManage 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 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

ModelWhat it represents
midaz.OrganizationA business entity that owns ledgers and accounts.
midaz.LedgerA collection of accounts and transactions.
midaz.AssetA unit of value (currency, token, etc.) that can be stored or moved.
midaz.AccountAn account for tracking assets and balances.
midaz.PortfolioA collection of accounts for grouping and management.
midaz.SegmentA categorization unit for granular organization.
midaz.TransactionA financial event composed of multiple operations.
midaz.OperationAn individual debit or credit entry within a transaction.
midaz.BalanceThe current state of an account’s holdings.
Need a builder, an internal request shape, or a deprecated type? Import github.com/LerianStudio/midaz-sdk-golang/v3/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 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

PackageWhat it solves
authAccess Manager OAuth and token lifecycle. Replaces the v2 access-manager package.
configCentralized config handling, env overrides, and custom service URLs.
concurrentTools for batching, rate-limiting, and worker pools.
errorsStructured error types, classifiers, and the canonical Retryable() predicate.
observabilityTracing, metrics, and logs through one OpenTelemetry provider.
retryRetry policy options with exponential backoff and jitter.
sdkctxPer-request context flags — idempotency keys, soft vs hard delete, include-deleted.
validationInput validation with clear, structured error messages.

Helper packages

PackageWhat it solves
accountsAccount-specific helpers and convenience functions.
conversionType conversion helpers between models and external formats.
dataData utilities and faker helpers for testing and demos.
formatUtilities for formatting data the Midaz way (dates, times, etc.).
generatorDemo and mass-data generation for end-to-end scenarios.
integrityChecksum and integrity verification utilities.
performanceHelpers for tuning bulk operations and high-throughput tasks.
securitySecurity utilities, including SSRF protection and TLS validation.
statsProcessing statistics and metrics aggregation.
transactionTransaction-building helpers and fluent builders.
utilsGeneral-purpose helpers used across the SDK.
versionSDK 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.
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.
package main

import (
	"context"
	"log"
	"os"

	"github.com/LerianStudio/midaz-sdk-golang/v3"
)

func main() {
	// Configure Access Manager. ClientID and ClientSecret are typically
	// loaded from environment variables — never hardcoded.
	c, err := midaz.New(
		midaz.WithEnvironment(midaz.EnvironmentProduction),
		midaz.WithAccessManager(midaz.AccessManager{
			Address:      "https://auth.midaz.io",
			ClientID:     os.Getenv("MIDAZ_CLIENT_ID"),
			ClientSecret: os.Getenv("MIDAZ_CLIENT_SECRET"),
		}),
	)
	if err != nil {
		log.Fatalf("midaz.New: %v", err)
	}
	defer c.Shutdown(context.Background())

	// Use c.Organizations, c.Ledgers, c.Accounts, ... as usual.
}
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:
c, err := midaz.New(
    midaz.WithEnvironment(midaz.EnvironmentLocal),
    midaz.WithAnonymous(),
)

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:
export PLUGIN_AUTH_ENABLED=true
export PLUGIN_AUTH_ADDRESS=https://your-auth-service.com
export MIDAZ_CLIENT_ID=your-client-id
export MIDAZ_CLIENT_SECRET=your-client-secret
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:
import "github.com/LerianStudio/midaz-sdk-golang/v3/pkg/config"

cfg, err := config.NewConfig(config.FromEnvironment())
if err != nil {
    log.Fatalf("config: %v", err)
}

c, err := midaz.New(midaz.WithConfig(cfg))
Environment loading is explicit in v3 — 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 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.
MethodReturnsUse when
ListXxx*models.ListResponse[T] (one page)You want exactly one page and decide when to advance.
ListXxxAlliter.Seq2[T, error]You want every item; the SDK handles paging internally.
ListXxxPagesiter.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 shapeEndpointsBase struct
Page-basedOrganizations, Ledgers, Assets, Portfolios, Segments, Accounts, AccountTypes, Balances, Holders, Aliasesmodels.PageListOpts{Limit, Page, SortDirection, StartDate, EndDate}
Cursor-basedTransactions, Operations, OperationRoutes, TransactionRoutes, AssetRatesmodels.CursorListOpts{Limit, Cursor, SortDirection, StartDate, EndDate}
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.
import (
    "github.com/LerianStudio/midaz-sdk-golang/v3"
    "github.com/LerianStudio/midaz-sdk-golang/v3/models"
)

opts := models.AccountsListOpts{
    PageListOpts: models.PageListOpts{Limit: 100},
    Filters: models.AccountsFilters{
        Status:    "ACTIVE",
        AssetCode: "USD",
    },
}

for account, err := range c.Accounts.ListAccountsAll(ctx, orgID, ledgerID, opts) {
    if err != nil {
        return fmt.Errorf("list accounts: %w", err)
    }
    process(account)
}

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.
opts := models.TransactionsListOpts{
    CursorListOpts: models.CursorListOpts{Limit: 50},
    Filters:        models.TransactionsFilters{Status: "APPROVED"},
}

for page, err := range c.Transactions.ListTransactionsPages(ctx, orgID, ledgerID, opts) {
    if err != nil {
        return fmt.Errorf("page iter: %w", err)
    }
    log.Printf("page=%d items=%d next_cursor=%q",
        page.Pagination.Page, len(page.Items), page.Pagination.NextCursor)

    for _, tx := range page.Items {
        process(tx)
    }

    if shouldStop(page) {
        break // The SDK aborts in-flight paging cleanly.
    }
}
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:
FieldWhat it carries
CategoryThe error class (validation, authentication, network, configuration, and so on).
CodeA stable string code suitable for branching or telemetry.
OperationThe SDK operation that produced the error (e.g. Accounts.GetAccount).
ResourceThe resource family the error refers to, when relevant.
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:
import (
    "errors"
    "fmt"

    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):
        // Matches both 401 and 403 — re-authenticate or fix permissions.
        return fmt.Errorf("auth failure: %w", err)
    case sdkerrors.IsValidationError(err):
        return fmt.Errorf("invalid input: %w", err)
    case sdkerrors.IsConflictError(err):
        return fmt.Errorf("already exists: %w", err)
    case sdkerrors.IsRateLimitError(err):
        return fmt.Errorf("rate limited: %w", err)
    case sdkerrors.IsNetworkError(err):
        return fmt.Errorf("transient transport: %w", err)
    case sdkerrors.IsConfigurationError(err):
        return fmt.Errorf("setup mistake: %w", err)
    }

    // Walk the structured fields when you need them.
    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())
    }
}

Drive retry decisions with Retryable()

The Error.Retryable() method is the canonical retry-policy source. Use it instead of building your own classification.
var sdkErr *sdkerrors.Error
if errors.As(err, &sdkErr) && sdkErr.Retryable() {
    // Apply your retry logic — backoff, jitter, max attempts, etc.
}

Wrap raw transport errors

If you’re calling lower-level HTTP code outside the SDK and want the same structured error shape, use ClassifyTransportError:
import sdkerrors "github.com/LerianStudio/midaz-sdk-golang/v3/pkg/errors"

resp, err := httpClient.Do(req)
if err != nil {
    return sdkerrors.ClassifyTransportError("PaymentService.Charge", err)
}

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.
import (
    "errors"
    "fmt"

    "github.com/LerianStudio/midaz-sdk-golang/v3/pkg/validation"
)

var fieldErrs *validation.FieldErrors
if errors.As(err, &fieldErrs) {
    for _, fe := range fieldErrs.Errs() {
        fmt.Printf("- %s: %s\n", fe.Field, fe.Message)
    }
}
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 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.
package main

import (
    "context"
    "log"
    "log/slog"
    "os"

    "github.com/LerianStudio/midaz-sdk-golang/v3"
)

func main() {
    logger := slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{
        Level: slog.LevelInfo,
    }))

    c, err := midaz.New(
        midaz.WithEnvironment(midaz.EnvironmentLocal),
        midaz.WithAnonymous(),
        midaz.WithLogger(logger),
    )
    if err != nil {
        log.Fatalf("midaz.New: %v", err)
    }
    defer c.Shutdown(context.Background())

    // SDK retry diagnostics, slow-call warnings, and other internal
    // log lines now flow through your handler.
}
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 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

package main

import (
    "context"
    "log"

    "github.com/LerianStudio/midaz-sdk-golang/v3"
    "github.com/LerianStudio/midaz-sdk-golang/v3/pkg/observability"
)

func main() {
    ctx := context.Background()

    provider, err := observability.New(ctx,
        observability.WithServiceName("payments-api"),
        observability.WithEnvironment("production"),
        observability.WithComponentEnabled(true, true, true), // tracing, metrics, logs
    )
    if err != nil {
        log.Fatalf("observability.New: %v", err)
    }
    defer provider.Shutdown(ctx)

    c, err := midaz.New(
        midaz.WithEnvironment(midaz.EnvironmentProduction),
        midaz.WithAccessManager(midaz.AccessManager{ /* ... */ }),
        midaz.WithObservabilityProvider(provider),
    )
    if err != nil {
        log.Fatalf("midaz.New: %v", err)
    }
    defer c.Shutdown(ctx)
}

Or pass options inline

import "github.com/LerianStudio/midaz-sdk-golang/v3/pkg/observability"

c, err := midaz.New(
    midaz.WithEnvironment(midaz.EnvironmentProduction),
    midaz.WithAccessManager(am),
    midaz.WithObservabilityOptions(
        observability.WithServiceName("payments-api"),
        observability.WithComponentEnabled(true, true, true),
    ),
)
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:
err = c.Trace("create-organization", func(ctx context.Context) error {
    _, err := c.Organizations.CreateOrganization(ctx, input)
    return err
})
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? Check the Tracing guide in the SDK repo for span attribute contracts, metric names, and exporter setup.

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

import "github.com/LerianStudio/midaz-sdk-golang/v3/pkg/sdkctx"

ctx := sdkctx.WithIdempotencyKey(context.Background(), "tx-2026-05-06-001")

tx, err := c.Transactions.CreateTransaction(ctx, orgID, ledgerID, input)

Suppress idempotency for one call

For rare fire-and-forget administrative endpoints, suppress the header per-request:
ctx := sdkctx.WithoutAutoIdempotency(context.Background())

err := c.SomeAdminService.DoOneShotThing(ctx, input)

Disable globally

If you don’t want auto-idempotency anywhere, turn it off at the client level:
c, err := midaz.New(
    midaz.WithEnvironment(midaz.EnvironmentLocal),
    midaz.WithAnonymous(),
    midaz.WithIdempotency(false),
)
You can also disable it via the 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.
VariableDescription
MIDAZ_ENVIRONMENTTarget environment (local, development, production).
MIDAZ_BASE_URLSingle base URL used when service-specific URLs are not set.
MIDAZ_ONBOARDING_URLOverride the onboarding service base URL.
MIDAZ_TRANSACTION_URLOverride the transaction service base URL.
MIDAZ_CRM_URLOverride the CRM service base URL (used by Holders and Aliases).
PLUGIN_AUTH_ENABLEDEnable Access Manager authentication (true or false).
PLUGIN_AUTH_ADDRESSAddress of the Access Manager service.
MIDAZ_CLIENT_IDClient ID for Access Manager authentication.
MIDAZ_CLIENT_SECRETClient secret for Access Manager authentication.
MIDAZ_TIMEOUTHTTP request timeout in seconds.
MIDAZ_USER_AGENTCustom User-Agent header sent with every request.
MIDAZ_DEBUGEnable debug logs (true or false).
MIDAZ_MAX_RETRIESMaximum retry attempts for failed requests.
MIDAZ_IDEMPOTENCYEnable automatic idempotency key generation for unsafe requests.
MIDAZ_SKIP_AUTH_CHECKSkip Access Manager config validation at startup (testing only).
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.
ExampleWhat it demonstrates
01-hello-worldMinimal init plus the first API call (~17 body lines).
02-authAccess Manager authentication (production setup).
03-end-to-endOrg → ledger → asset → account → transaction.
04-listing-cursorCursor-based pagination with iter.Seq2.
05-listing-pagesPage-based pagination — List / ListAll / ListPages.
06-idempotencyAuto / explicit / suppressed idempotency modes.
07-retriesDefault policy, custom policy, disabled retries.
08-logging-slog*slog.Logger integration (v3 logging).
09-testing-with-mocksgo.uber.org/mock for unit testing your code against the SDK.
10-observability-otelFull OpenTelemetry surface (tracing + metrics + logs).
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


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), 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 full walk-through with side-by-side v2 / v3 code on every breaking change, read the Migration guide from v2 to v3.

Explore the APIs


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

External API mapping

Internal API mapping

Godoc documentation