> ## 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.

# SDK de Midaz para Go

> Crea aplicaciones en Go sobre Midaz con el SDK oficial v4: paginación tipada, errores estructurados y observabilidad con OpenTelemetry lista para usar.

El **SDK de Midaz para Go** es el cliente idiomático v4 para las API del ledger financiero de Midaz. Te da acceso tipado a todos los servicios (Organizations, Ledgers, Accounts, Transactions y más) con una sola superficie para autenticación, paginación, errores, registro y observabilidad.

<Warning>
  **¿Vienes de v2?** v4 es una versión mayor de corte limpio con cambios incompatibles en autenticación, paginación, errores y acceso a servicios. No hay ventana de desuso. Cambias tu import de `/v2` a `/v4` y migras al mismo tiempo.

  Consulta [Migración desde v2](#migrating-from-v2) antes de actualizar.
</Warning>

## Primeros pasos

***

### Paso 1 – Instala Go

Antes de usar el SDK, **debes** instalar Go en tu máquina. v4 declara **Go 1.26** en `go.mod`. La API pública también usa `iter.Seq2` y `log/slog`.

<Steps>
  <Step>
    Ve al [sitio oficial de Go](https://golang.org/dl/).
  </Step>

  <Step>
    Descarga el instalador para tu sistema operativo (Windows, macOS o Linux).
  </Step>

  <Step>
    [**Sigue las instrucciones de instalación.**](https://go.dev/doc/install)
  </Step>
</Steps>

### Paso 2 – Crea o usa un proyecto de Go existente

**Crea un proyecto de Go:**

Para crear un proyecto de Go, usa el siguiente comando:

<CodeGroup>
  ```bash Bash theme={null}
  mkdir my-midaz-app
  cd my-midaz-app
  go mod init my-midaz-app
  ```
</CodeGroup>

**Usa un proyecto de Go existente:**

Si trabajas en un proyecto existente, confirma que haya un archivo `go.mod` en la raíz. Si no lo hay, ejecuta el siguiente comando para crear uno:

<CodeGroup>
  ```bash Bash theme={null}
  go mod init your-module-name
  ```
</CodeGroup>

### Paso 3 – Agrega el SDK de Midaz

Dentro del directorio de tu proyecto, ejecuta el siguiente comando para obtener el SDK v4 y agregarlo a tus archivos `go.mod` y `go.sum`:

<Warning>
  **La ruta del módulo requiere el sufijo `/v4`.** Si lo omites, Go resolverá una versión anterior a v4 desactualizada a la que le falta cada cambio incluido en esta versión. Importa siempre `github.com/LerianStudio/midaz-sdk-golang/v4`.
</Warning>

<CodeGroup>
  ```bash Bash theme={null}
  go get github.com/LerianStudio/midaz-sdk-golang/v4
  ```
</CodeGroup>

<Tip>
  En VS Code o GoLand, tu IDE puede ejecutar `go get` automáticamente cuando importas un paquete nuevo.
</Tip>

### Paso 4 – Importa el SDK

Crea o abre un archivo `main.go` y agrega el siguiente contenido. El ejemplo a continuación crea un cliente contra tu stack local de Midaz con autenticación anónima, lista organizaciones y luego crea una nueva.

<Note>
  El ejemplo a continuación apunta a un stack local de Midaz con la autenticación deshabilitada. Si aún no tienes uno en ejecución, consulta [Primeros pasos con Midaz](/es/start-here/getting-started) para ponerlo en marcha antes de ejecutar el fragmento.
</Note>

<CodeGroup>
  ```go Go expandable theme={null}
  package main

  import (
  	"context"
  	"fmt"
  	"log"

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

  func main() {
  	// Build a client. v4 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)
  }
  ```
</CodeGroup>

Esto te da acceso a:

* El cliente de Midaz para llamar a todos los servicios de la API.
* Modelos de datos integrados (como `CreateOrganizationInput`).
* Autenticación mediante **Access Manager** (producción) o **Anonymous** (desarrollo local).
* Un sistema de configuración tipado que falla rápido en el momento de la construcción.

<Tip>
  Para la configuración completa, consulta la sección [Autenticación](#authentication).
</Tip>

### Paso 5 – Ejecuta el proyecto

Ejecuta el siguiente comando:

<CodeGroup>
  ```bash Bash theme={null}
  go run main.go
  ```
</CodeGroup>

## Arquitectura del SDK

***

Cada servicio es accesible directamente desde el cliente. Cada método de listado sigue la misma forma de trío. Cada error está estructurado. Cada opción falla rápido en el momento de la construcción.

#### Diseño en capas

| Capa                       | Qué maneja                                                                                                           |
| :------------------------- | :------------------------------------------------------------------------------------------------------------------- |
| **Cliente**                | El punto de entrada principal — `midaz.New(...)` conecta la autenticación, los reintentos y la observabilidad.       |
| **Servicios**              | Acceso de alto nivel a cada dominio de Midaz, expuesto como campos promovidos en el cliente.                         |
| **Modelos**                | Las estructuras de datos principales que reflejan la lógica de dominio de Midaz, reexportadas en el paquete `midaz`. |
| **Paquetes de utilidades** | Ayudantes modulares para configuración, errores, observabilidad, reintentos, idempotencia y más.                     |

<Note>
  Los servicios se acceden directamente desde el cliente: `c.Accounts`, `c.Transactions`, `c.Organizations`. Es posible que todavía veas un campo `Entity` incrustado en el autocompletado, pero el código nuevo debe usar los campos de servicio directos que se muestran en esta guía.
</Note>

<Tip>
  Para conocer el diseño detrás de la reescritura, consulta la [guía de arquitectura](https://github.com/LerianStudio/midaz-sdk-golang/blob/main/docs/architecture.md).
</Tip>

### Servicios

La capa `Services` es tu punto de acceso a cada dominio de Midaz. Cada servicio maneja una familia de recursos y ofrece todos sus métodos como parte de una única interfaz. No hay un interruptor `UseAllAPIs()` ni un paso de registro de servicios. Cada servicio está listo para usarse en cuanto `midaz.New()` retorna.

#### Servicios disponibles

| Servicio              | Qué hace                                                          |
| :-------------------- | :---------------------------------------------------------------- |
| `c.Organizations`     | Gestiona organizaciones.                                          |
| `c.Ledgers`           | Crea y recupera ledgers.                                          |
| `c.Assets`            | Define y gestiona activos.                                        |
| `c.AssetRates`        | Configura y obtiene tasas de cambio de activos.                   |
| `c.Accounts`          | Gestiona cuentas y consulta saldos.                               |
| `c.AccountTypes`      | Gestiona definiciones de tipos de cuenta.                         |
| `c.Portfolios`        | Agrupa cuentas en portafolios.                                    |
| `c.Segments`          | Categoriza cuentas usando segmentos.                              |
| `c.Transactions`      | Crea y busca transacciones financieras.                           |
| `c.TransactionRoutes` | Define y gestiona reglas de rutas de transacción.                 |
| `c.Operations`        | Profundiza en las operaciones atómicas dentro de una transacción. |
| `c.OperationRoutes`   | Define y gestiona reglas de rutas de operación.                   |
| `c.Balances`          | Obtiene saldos de cuenta en tiempo real.                          |
| `c.Holders`           | Gestiona titulares de cuenta de CRM.                              |
| `c.Aliases`           | Gestiona alias de CRM para cuentas y entidades.                   |
| `c.MetadataIndexes`   | Gestiona índices de metadatos con capacidad de búsqueda.          |

### Modelos

Cada tipo de modelo está estrechamente ligado a un concepto de negocio del mundo real. Los usarás en cada llamada de servicio, desde el alta de cuentas hasta el registro de transacciones con múltiples partidas.

En v4, los tipos de modelo más comunes se reexportan directamente en el paquete `midaz`. Esto significa que `midaz.Account` y `models.Account` son el mismo tipo, y la mayoría del código solo necesita un import.

#### Tipos de modelo comunes

| Modelo               | Qué representa                                                            |
| :------------------- | :------------------------------------------------------------------------ |
| `midaz.Organization` | Una entidad de negocio que posee ledgers y cuentas.                       |
| `midaz.Ledger`       | Una colección de cuentas y transacciones.                                 |
| `midaz.Asset`        | Una unidad de valor (moneda, token, etc.) que se puede almacenar o mover. |
| `midaz.Account`      | Una cuenta para el seguimiento de activos y saldos.                       |
| `midaz.Portfolio`    | Una colección de cuentas para agrupación y gestión.                       |
| `midaz.Segment`      | Una unidad de categorización para una organización granular.              |
| `midaz.Transaction`  | Un evento financiero compuesto por varias operaciones.                    |
| `midaz.Operation`    | Un asiento individual de débito o crédito dentro de una transacción.      |
| `midaz.Balance`      | El estado actual de las tenencias de una cuenta.                          |

<Tip>
  Para un builder, una forma de solicitud interna o un tipo obsoleto, importa `github.com/LerianStudio/midaz-sdk-golang/v4/models` directamente. Todos los tipos viven ahí, y los alias de `midaz` conservan la identidad de tipo, así que las dos rutas de import interoperan sin problemas.
</Tip>

### Paquetes de utilidades

Dentro de la carpeta `pkg` del SDK, encontrarás paquetes de utilidades para el manejo de configuración, políticas de reintento y primitivas de seguridad. Se dividen en dos grupos. Los paquetes **Core** atienden preocupaciones transversales del SDK. Los paquetes **Helper** ofrecen utilidades específicas de dominio o de bajo nivel.

#### Paquetes Core

| Paquete         | Qué resuelve                                                                                                          |
| :-------------- | :-------------------------------------------------------------------------------------------------------------------- |
| `auth`          | OAuth de Access Manager y el ciclo de vida del token. Reemplaza el paquete `access-manager` de v2.                    |
| `config`        | Manejo centralizado de configuración, sobrescrituras por entorno y URL de servicio personalizadas.                    |
| `concurrent`    | Herramientas para procesamiento por lotes, límite de tasa y pools de workers.                                         |
| `errors`        | Tipos de error estructurados, clasificadores y el predicado canónico `Retryable()`.                                   |
| `observability` | Trazas, métricas y logs a través de un único proveedor de OpenTelemetry.                                              |
| `retry`         | Opciones de política de reintento con backoff exponencial y jitter.                                                   |
| `sdkctx`        | Indicadores de contexto por solicitud: claves de idempotencia, borrado suave frente a definitivo, incluir eliminados. |
| `validation`    | Validación de entradas con mensajes de error claros y estructurados.                                                  |

#### Paquetes Helper

| Paquete       | Qué resuelve                                                                        |
| :------------ | :---------------------------------------------------------------------------------- |
| `accounts`    | Ayudantes específicos de cuentas y funciones de conveniencia.                       |
| `conversion`  | Ayudantes de conversión de tipos entre modelos y formatos externos.                 |
| `data`        | Utilidades de datos y ayudantes faker para pruebas y demos.                         |
| `format`      | Utilidades para dar formato a los datos al estilo Midaz (fechas, horas, etc.).      |
| `generator`   | Generación de datos de demo y masivos para escenarios de extremo a extremo.         |
| `integrity`   | Utilidades de checksum y verificación de integridad.                                |
| `performance` | Ayudantes para ajustar operaciones masivas y tareas de alto rendimiento.            |
| `security`    | Utilidades de seguridad, incluida la protección contra SSRF y la validación de TLS. |
| `stats`       | Estadísticas de procesamiento y agregación de métricas.                             |
| `transaction` | Ayudantes para construir transacciones y builders fluidos.                          |
| `utils`       | Ayudantes de propósito general usados en todo el SDK.                               |
| `version`     | Metadatos e identificación de la versión del SDK.                                   |

<Note>
  El paquete `pkg/access-manager` de v2 se movió a `pkg/auth` (así el directorio coincide con el nombre del paquete). El paquete `pkg/pagination` de v2 se eliminó. Su superficie vive hoy en `models` y en cada servicio.
</Note>

<h2 id="authentication">
  Autenticación
</h2>

***

En v4, el SDK requiere exactamente una fuente de autenticación en el momento de la construcción. Llamar a `midaz.New(...)` sin ninguna de las dos retorna un error de configuración tipado. Ya no hay cascadas silenciosas de 401 en la primera llamada a la API.

Tienes dos opciones:

* **`midaz.WithAccessManager(...)`**: OAuth con forma de producción a través del Lerian Access Manager. Se recomienda para cualquier stack que no sea local.
* **`midaz.WithAnonymous()`**: renuncia por completo a la autenticación. Solo es adecuado para un stack local de Midaz con la autenticación deshabilitada.

Las dos opciones son mutuamente excluyentes.

### Producción: Access Manager

Conecta tus credenciales de Access Manager en `midaz.WithAccessManager`. El SDK obtiene un token inicial de forma anticipada en el momento de la construcción, así que las configuraciones incorrectas aparecen como errores de configuración en lugar de cascadas de 401.

<Warning>
  Reemplaza los valores del bloque `// Configure Access Manager` con tus propias credenciales antes de ejecutar.
</Warning>

<CodeGroup>
  ```go Go expandable theme={null}
  package main

  import (
  	"context"
  	"log"
  	"os"

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

  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.
  }
  ```
</CodeGroup>

El SDK solicita un token a tu Access Manager, lo adjunta a cada llamada a la API y lo renueva automáticamente cuando expira.

### Desarrollo local: Anonymous

Para un stack local de Midaz con la autenticación deshabilitada, renuncia a ella de forma explícita:

<CodeGroup>
  ```go Go theme={null}
  c, err := midaz.New(
      midaz.WithEnvironment(midaz.EnvironmentLocal),
      midaz.WithAnonymous(),
  )
  ```
</CodeGroup>

### Configura mediante variables de entorno

También puedes apuntar el SDK a tu Access Manager mediante variables de entorno. Expórtalas en tu shell o en tu gestor de procesos:

<CodeGroup>
  ```bash Bash theme={null}
  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
  ```
</CodeGroup>

<Note>
  `config.FromEnvironment()` lee el **entorno del proceso**, no un archivo `.env`. Si mantienes las variables en un archivo `.env` durante el desarrollo, cárgalas con una biblioteca como `godotenv` antes de llamar a `config.NewConfig(config.FromEnvironment())`.
</Note>

Luego activa la carga desde el entorno en el momento de la configuración:

<CodeGroup>
  ```go Go theme={null}
  import "github.com/LerianStudio/midaz-sdk-golang/v4/pkg/config"

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

  c, err := midaz.New(midaz.WithConfig(cfg))
  ```
</CodeGroup>

<Note>
  La carga desde el entorno es **explícita** en v4. `config.FromEnvironment()` debe estar en la cadena de opciones. El SDK ya no lee variables de entorno de forma implícita durante la construcción.
</Note>

<Tip>
  Para el recorrido completo de autenticación, consulta la [guía de autenticación](https://github.com/LerianStudio/midaz-sdk-golang/blob/main/docs/auth.md) en el repositorio del SDK.
</Tip>

## Multi-tenancy

***

**El ámbito del tenant proviene de las claims de Access Manager o JWT** usadas para obtener el token. El SDK aplica la identidad del tenant automáticamente a partir de esas claims. El lado del cliente no necesita configuración adicional.

Para ejecutar llamadas bajo un ámbito de tenant diferente, usa un conjunto distinto de credenciales de Access Manager, o crea un segundo cliente con su propio contexto de token.

## Listado e iteración

***

Cada endpoint de listado en v4 viene en tres variantes. Elige la que se ajuste a tu caso de uso. Son consistentes en todos los servicios.

| Método         | Retorna                                | Úsalo cuando                                                                            |
| :------------- | :------------------------------------- | :-------------------------------------------------------------------------------------- |
| `ListXxx`      | `*models.ListResponse[T]` (una página) | Quieres exactamente una página y decides cuándo avanzar.                                |
| `ListXxxAll`   | `iter.Seq2[T, error]`                  | Quieres todos los elementos; el SDK maneja la paginación internamente.                  |
| `ListXxxPages` | `iter.Seq2[*ListResponse[T], error]`   | Necesitas metadatos en el nivel de página para checkpointing o procesamiento por lotes. |

### Opciones de listado tipadas

Cada método de listado recibe una struct de opciones tipada que incorpora una de dos structs base, según cómo pagine el endpoint:

| Forma de paginación  | Endpoints                                                                                                | Struct base                                                               |
| :------------------- | :------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------ |
| **Basada en página** | Organizations, Ledgers, Assets, Portfolios, Segments, Accounts, AccountTypes, Balances, Holders, Aliases | `models.PageListOpts{Limit, Page, SortDirection, StartDate, EndDate}`     |
| **Basada en cursor** | Transactions, Operations, OperationRoutes, TransactionRoutes, AssetRates                                 | `models.CursorListOpts{Limit, Cursor, SortDirection, StartDate, EndDate}` |

Cada endpoint también expone una subestructura `Filters` tipada, con solo los campos que ese endpoint realmente admite. Establecer un campo en la forma incorrecta (por ejemplo, `Page` en un endpoint basado en cursor) falla en tiempo de compilación, no de forma silenciosa en tiempo de ejecución.

### Itera cada elemento con `ListAll`

La forma más idiomática es un bucle `range` sobre cada elemento en todas las páginas. El SDK avanza los cursores y obtiene las páginas internamente.

<CodeGroup>
  ```go Go theme={null}
  import (
      "github.com/LerianStudio/midaz-sdk-golang/v4"
      "github.com/LerianStudio/midaz-sdk-golang/v4/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)
  }
  ```
</CodeGroup>

### Itera los sobres de página con `ListPages`

Cuando necesitas metadatos en el nivel de página (para checkpointing, procesamiento por lotes o detenerte a mitad de página), itera sobre `ListXxxPages` en su lugar. Cada entrada es un `*ListResponse[T]` con el bloque `Pagination` completo adjunto.

<CodeGroup>
  ```go Go theme={null}
  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.
      }
  }
  ```
</CodeGroup>

Consulta la [guía de paginación](https://github.com/LerianStudio/midaz-sdk-golang/blob/main/docs/pagination.md) en el repositorio del SDK para conocer la semántica de `HasMore()`, el manejo de `NextCursor` y la tabla de decisión entre página y cursor.

## Manejo de errores

***

La mayoría de los errores que retorna la capa de servicios del SDK son `*pkg/errors.Error` con campos estructurados:

| Campo       | Qué contiene                                                                      |
| :---------- | :-------------------------------------------------------------------------------- |
| `Category`  | La clase de error (validación, autenticación, red, configuración, etc.).          |
| `Code`      | Un código de cadena estable, adecuado para ramificar la lógica o para telemetría. |
| `Operation` | La operación del SDK que produjo el error (por ejemplo, `Accounts.GetAccount`).   |
| `Resource`  | La familia de recursos a la que se refiere el error, cuando corresponde.          |

Puedes ramificar la lógica con predicados tipados, recorrer los campos con `errors.As`, o usar el método canónico `Retryable()` para guiar la política de reintentos.

### Ramifica la lógica con predicados tipados

El SDK incluye un conjunto completo de predicados `Is*` para que puedas identificar errores sin acceder a los internos:

<CodeGroup>
  ```go Go expandable theme={null}
  import (
      "errors"
      "fmt"

      sdkerrors "github.com/LerianStudio/midaz-sdk-golang/v4/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())
      }
  }
  ```
</CodeGroup>

### Guía las decisiones de reintento con `Retryable()`

El método `Error.Retryable()` es la fuente canónica para la política de reintentos. Úsalo en lugar de construir tu propia clasificación.

<CodeGroup>
  ```go Go theme={null}
  var sdkErr *sdkerrors.Error
  if errors.As(err, &sdkErr) && sdkErr.Retryable() {
      // Apply your retry logic — backoff, jitter, max attempts, etc.
  }
  ```
</CodeGroup>

### Envuelve errores de transporte sin procesar

Si llamas a código HTTP de nivel más bajo fuera del SDK y quieres la misma forma de error estructurado, usa `ClassifyTransportError`:

<CodeGroup>
  ```go Go theme={null}
  import sdkerrors "github.com/LerianStudio/midaz-sdk-golang/v4/pkg/errors"

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

### Validación local: FieldErrors

La validación local de entradas expone `*pkg/validation.FieldErrors`, una colección estructurada de observaciones por campo generadas por el SDK antes de cualquier llamada HTTP. Usa `errors.As` para inspeccionarlas al validar la entrada del usuario o los builders.

<CodeGroup>
  ```go Go theme={null}
  import (
      "errors"
      "fmt"

      "github.com/LerianStudio/midaz-sdk-golang/v4/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)
      }
  }
  ```
</CodeGroup>

<Tip>
  Para el mapa completo de categorías, todos los códigos y la semántica de los límites de reintento, consulta la [guía de manejo de errores](https://github.com/LerianStudio/midaz-sdk-golang/blob/main/docs/errors.md) en el repositorio del SDK.
</Tip>

## Registro

***

En v4, **`*slog.Logger` es la superficie canónica de logger**. Conéctalo mediante `midaz.WithLogger(...)`. El SDK es **silencioso de forma predeterminada**. Usa `slog.DiscardHandler` hasta que lo actives.

Tú decides el handler, el nivel y el destino.

<CodeGroup>
  ```go Go expandable theme={null}
  package main

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

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

  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.
  }
  ```
</CodeGroup>

zap, zerolog y charmbracelet/log se integran como adaptadores `slog.Handler`. El SDK funciona con cualquier backend que hable slog.

<Tip>
  La [guía de registro](https://github.com/LerianStudio/midaz-sdk-golang/blob/main/docs/logging.md) tiene recetas de adaptadores para todas las bibliotecas de registro comunes.
</Tip>

## Observabilidad

***

OpenTelemetry es de primera clase en v4. Un único proveedor de observabilidad te da spans, métricas y logs correlacionados con OTel mediante un solo punto de conexión.

Configura la observabilidad pasando un proveedor completamente construido, o pasando opciones que el SDK ensambla por ti.

### Conecta un proveedor

<CodeGroup>
  ```go Go expandable theme={null}
  package main

  import (
      "context"
      "log"

      "github.com/LerianStudio/midaz-sdk-golang/v4"
      "github.com/LerianStudio/midaz-sdk-golang/v4/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)
  }
  ```
</CodeGroup>

### O pasa opciones en línea

<CodeGroup>
  ```go Go theme={null}
  import "github.com/LerianStudio/midaz-sdk-golang/v4/pkg/observability"

  c, err := midaz.New(
      midaz.WithEnvironment(midaz.EnvironmentProduction),
      midaz.WithAccessManager(am),
      midaz.WithObservabilityOptions(
          observability.WithServiceName("payments-api"),
          observability.WithComponentEnabled(true, true, true),
      ),
  )
  ```
</CodeGroup>

El SDK emite un span HTTP por cada solicitud saliente, con la propagación correcta de `traceparent` de W3C. Los registros de log de negocio llevan solo IDs seguros, nunca payloads, nombres, direcciones o encabezados de autenticación.

También puedes envolver un bloque de lógica de negocio en un span mediante `Client.Trace`:

<CodeGroup>
  ```go Go theme={null}
  err = c.Trace("create-organization", func(ctx context.Context) error {
      _, err := c.Organizations.CreateOrganization(ctx, input)
      return err
  })
  ```
</CodeGroup>

<Warning>
  `WithObservabilityOptions` y `WithObservabilityProvider` usan **semántica de reemplazo**, no de fusión. Cada llamada reemplaza cualquier proveedor instalado antes. Para partir de una base, incluye `observability.WithDevelopmentDefaults` u `observability.WithProductionDefaults` como primera opción en la cadena.
</Warning>

<Tip>
  Consulta el [ejemplo `10-observability-otel`](https://github.com/LerianStudio/midaz-sdk-golang/tree/main/examples/10-observability-otel) en el repositorio del SDK para ver los atributos de span, los nombres de métrica y la configuración del exportador.
</Tip>

## Idempotencia

***

La idempotencia automática está **activada de forma predeterminada** en v4. El SDK emite un encabezado `X-Idempotency: <uuid>` en cada solicitud HTTP no segura (POST, PUT, PATCH, DELETE). Así, los reintentos ante fallos transitorios no crean recursos duplicados.

Puedes sobrescribir la clave generada automáticamente en cada llamada cuando necesitas una clave estable proporcionada por quien llama. Esto es típico en pasos de saga, filas de outbox o envíos impulsados por la interfaz.

### Define una clave estable por solicitud

<CodeGroup>
  ```go Go theme={null}
  import "github.com/LerianStudio/midaz-sdk-golang/v4/pkg/sdkctx"

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

  tx, err := c.Transactions.CreateTransaction(ctx, orgID, ledgerID, input)
  ```
</CodeGroup>

### Suprime la idempotencia para una llamada

Para endpoints administrativos poco frecuentes de tipo fire-and-forget, suprime el encabezado por solicitud:

<CodeGroup>
  ```go Go theme={null}
  ctx := sdkctx.WithoutAutoIdempotency(context.Background())

  err := c.SomeAdminService.DoOneShotThing(ctx, input)
  ```
</CodeGroup>

### Deshabilita de forma global

Si no quieres la idempotencia automática en ningún lugar, desactívala en el nivel del cliente:

<CodeGroup>
  ```go Go theme={null}
  c, err := midaz.New(
      midaz.WithEnvironment(midaz.EnvironmentLocal),
      midaz.WithAnonymous(),
      midaz.WithIdempotency(false),
  )
  ```
</CodeGroup>

También puedes deshabilitarla mediante la variable de entorno `MIDAZ_IDEMPOTENCY=false` cuando usas `config.FromEnvironment()`.

## Variables de entorno

***

Puedes configurar el SDK con variables de entorno en lugar de valores fijos en el código. La carga desde el entorno es **explícita** en v4. Pasa `config.FromEnvironment()` en tu cadena de opciones de configuración para activarla.

| Variable              | Descripción                                                                                                  |
| :-------------------- | :----------------------------------------------------------------------------------------------------------- |
| `MIDAZ_ENVIRONMENT`   | Entorno de destino (`local`, `development`, `production`).                                                   |
| `MIDAZ_BASE_URL`      | URL base para todos los servicios de Midaz, usada cuando no se definen las URL específicas de cada servicio. |
| `MIDAZ_LEDGER_URL`    | URL de la API del ledger. Atiende los endpoints de onboarding y de transacciones.                            |
| `MIDAZ_CRM_URL`       | URL de la API de CRM. La usan Holders y Aliases.                                                             |
| `PLUGIN_AUTH_ENABLED` | Habilita la autenticación de Access Manager (`true` o `false`).                                              |
| `PLUGIN_AUTH_ADDRESS` | Dirección del servicio de Access Manager.                                                                    |
| `MIDAZ_CLIENT_ID`     | ID de cliente para la autenticación de Access Manager.                                                       |
| `MIDAZ_CLIENT_SECRET` | Secreto de cliente para la autenticación de Access Manager.                                                  |
| `MIDAZ_TIMEOUT`       | Tiempo de espera de la solicitud HTTP, en segundos.                                                          |
| `MIDAZ_DEBUG`         | Habilita los logs de depuración (`true` o `false`).                                                          |
| `MIDAZ_MAX_RETRIES`   | Cantidad máxima de reintentos para solicitudes fallidas. Define `0` para deshabilitar los reintentos.        |
| `MIDAZ_IDEMPOTENCY`   | Habilita claves de idempotencia automáticas para solicitudes no seguras (`true` o `false`).                  |

Para la matriz completa de opciones de `midaz`, `pkg/config` y `pkg/sdkctx`, consulta la [guía de configuración](https://github.com/LerianStudio/midaz-sdk-golang/blob/main/docs/configuration.md) en el repositorio del SDK.

## Proyectos de ejemplo

***

<Tip>
  El SDK incluye un recorrido numerado por sus capacidades principales (ejemplos 01–10), además de un conjunto de ejemplos avanzados y de referencia. El conjunto numerado son **tutoriales enfocados**: cada uno enseña exactamente un concepto con el cuerpo más pequeño posible. Explóralos en el [directorio de ejemplos](https://github.com/LerianStudio/midaz-sdk-golang/tree/main/examples) en GitHub.
</Tip>

<Columns cols={2}>
  | Ejemplo                 | Qué demuestra                                                                  |
  | :---------------------- | :----------------------------------------------------------------------------- |
  | `01-hello-world`        | Inicialización mínima más la primera llamada a la API (\~17 líneas de cuerpo). |
  | `02-auth`               | Autenticación con Access Manager (configuración de producción).                |
  | `03-end-to-end`         | Organización → ledger → activo → cuenta → transacción.                         |
  | `04-listing-cursor`     | Paginación basada en cursor con `iter.Seq2`.                                   |
  | `05-listing-pages`      | Paginación basada en página — `List` / `ListAll` / `ListPages`.                |
  | `06-idempotency`        | Modos de idempotencia automático, explícito y suprimido.                       |
  | `07-retries`            | Política predeterminada, política personalizada, reintentos deshabilitados.    |
  | `08-logging-slog`       | Integración con `*slog.Logger` (registro en v4).                               |
  | `09-testing-with-mocks` | `go.uber.org/mock` para pruebas unitarias de tu código contra el SDK.          |
  | `10-observability-otel` | Superficie completa de OpenTelemetry (trazas + métricas + logs).               |
</Columns>

El repositorio también incluye ejemplos especializados y de referencia: `concurrency`, `configuration`, `context`, `tracing`, `tracing-server`, `pkg-validation-demo`, `mass-demo-generator` y `workflow-with-entities`. Cubren patrones avanzados (paralelismo acotado, propagación de contexto de OTel entre procesos, generación masiva de datos) para cuando ya superaste el conjunto enfocado.

<h2 id="migrating-from-v2">
  Migración desde v2
</h2>

***

v4 es una **versión mayor de corte limpio, sin ventana de desuso**. No hay una versión de transición, ni un shim `// Deprecated:`, ni un alias retrocompatible de la superficie anterior. Cambia tu import de `/v2` a `/v4` y revisa los cambios incompatibles a continuación.

Los cambios más grandes que debes planear:

* **Ruta del módulo y nombre del paquete**: `github.com/LerianStudio/midaz-sdk-golang/v4` (antes `/v2`), paquete `midaz` (antes `client`).
* **Autenticación**: `WithAuthToken` desapareció. Usa `WithAccessManager` para producción o `WithAnonymous` para stacks locales. Se requiere una fuente de autenticación en el momento de la construcción.
* **Acceso a servicios**: `c.Accounts.X` (antes `c.Entity.Accounts.X`). El campo `c.Entity` todavía existe por compatibilidad, pero todos los ejemplos usan la forma corta.
* **Paginación**: `models.ListOptions` y sus 30 setters fluidos desaparecieron. Usa las opciones tipadas por endpoint (`models.AccountsListOpts`, `models.TransactionsListOpts`, ...) y los nuevos iteradores `ListAll` / `ListPages`.
* **Errores**: `*MidazError` desapareció. Los errores de la capa de servicios ahora usan `*pkg/errors.Error`, con `Retryable()` como fuente oficial de reintentos. La validación local puede exponer `*pkg/validation.FieldErrors`.
* **Identidad del tenant**: `WithTenantID`, la variable de entorno `MIDAZ_TENANT_ID` y el encabezado `X-Tenant-ID` desaparecieron. El ámbito del tenant fluye a través de las claims de Access Manager o JWT.
* **Registro**: `*slog.Logger` reemplaza a la interfaz personalizada `observability.Logger` como superficie canónica. El SDK es silencioso de forma predeterminada.

Para conocer los cambios de autenticación en detalle, consulta la [guía de autenticación](https://github.com/LerianStudio/midaz-sdk-golang/blob/main/docs/auth.md) en el repositorio del SDK. El [directorio de ejemplos](https://github.com/LerianStudio/midaz-sdk-golang/tree/main/examples) muestra la API v4 en la práctica.

## Explora las API

***

Para más información sobre las API, consulta los siguientes enlaces:

<Columns cols={2}>
  <Card title="Mapeo de la API externa" icon="link" horizontal href="https://github.com/LerianStudio/midaz-sdk-golang/blob/main/docs/mapping/external_apis.md" />

  <Card title="Mapeo de la API interna" icon="link" horizontal href="https://github.com/LerianStudio/midaz-sdk-golang/blob/main/docs/mapping/internal_apis.md" />

  <Card title="Documentación de Godoc" icon="books" horizontal href="https://pkg.go.dev/github.com/LerianStudio/midaz-sdk-golang/v4" />
</Columns>
