> ## Documentation Index
> Fetch the complete documentation index at: https://docs.origoid.com/llms.txt
> Use this file to discover all available pages before exploring further.

# SDK Go

> Cliente oficial Go. Context-aware, idiomático, distribuido via Go modules.

El SDK de Go se distribuye como un módulo Go estándar. No hay
registry separado — `go get` baja el código directo de GitHub. El SDK
provee una función tipada por cada endpoint de OrigoID y sigue
patrones Go idiomáticos (`context.Context`, structs exportados,
errores explícitos).

## Instalación

```bash theme={null}
go get github.com/origoid/sdk-go@v0.1.0
```

O agrega a `go.mod`:

```go theme={null}
require github.com/origoid/sdk-go v0.1.0
```

Luego corre `go mod tidy`. Código en
[github.com/origoid/sdk-go](https://github.com/origoid/sdk-go)
(público, para auditar). Requiere Go **1.21 o más reciente**.

## Inicializa el cliente

```go theme={null}
package main

import (
    "fmt"
    "os"

    "github.com/origoid/sdk-go/client"
    "github.com/origoid/sdk-go/option"
)

func main() {
    c := client.NewClient(
        option.WithAPIKey(os.Getenv("ORIGOID_API_KEY")),
    )
    _ = c
    fmt.Println("client ready")
}
```

Ese es todo el setup — no hay nada más que configurar. Nunca
hardcodees la key. Léela de `os.Getenv`, [`viper`](https://github.com/spf13/viper),
o un secrets manager (HashiCorp Vault, 1Password, Doppler, etc.).

## Tu primera llamada

El ejemplo usa `PELJ900101HDFRRN09`, un **CURP sintético de los
ejemplos del OpenAPI** — no es CURP de persona real. Reemplázalo con
el CURP que necesites validar.

```go theme={null}
package main

import (
    "context"
    "fmt"
    "log"
    "os"

    origoid "github.com/origoid/sdk-go"
    "github.com/origoid/sdk-go/client"
    "github.com/origoid/sdk-go/option"
)

func main() {
    c := client.NewClient(
        option.WithAPIKey(os.Getenv("ORIGOID_API_KEY")),
    )

    env, err := c.Renapo.ValidateCurp(
        context.Background(),
        &origoid.ValidateCurpRequest{
            Curp: "PELJ900101HDFRRN09", // sintético — reemplazar con input real
        },
    )
    if err != nil {
        log.Fatal(err)
    }

    if env.Status == "OK" && env.Type == "SUCCESS" {
        // env.Data trae el registro RENAPO.
        fmt.Println("Titular del CURP:", env.Data)
    } else {
        // Otro tipo de resultado — revisa el catálogo de respuestas del endpoint para tu lógica.
        fmt.Println(env.Type, "—", env.Message)
    }
}
```

Cada método devuelve `(*origoid.Envelope, error)`. La shape del
envelope es `{ Status, Type, Message, Data, TransactionId,
ProcessedAt, Billable, Errors }`. Ver [Envelope de respuesta](/es/envelope)
para el contrato.

## Métodos por recurso

El cliente agrupa operaciones por dominio regulatorio.

### `c.Authentication`

```go theme={null}
c.Authentication.IssueToken(ctx, &origoid.IssueTokenRequest{ExpireAfter: 1800})
```

### `c.Renapo`

```go theme={null}
c.Renapo.ValidateCurp(ctx, &origoid.ValidateCurpRequest{
    Curp: "PELJ900101HDFRRN09",
})

c.Renapo.LookupCurp(ctx, &origoid.LookupCurpRequest{
    GivenNames:     "JUAN",
    FirstSurname:   "PEREZ",
    SecondSurname:  origoid.String("LOPEZ"),
    DateOfBirth:    "1990-01-01",
    Gender:         origoid.LookupCurpRequestGenderH,
    BirthStateCode: "DF",
})
```

`origoid.String(...)` es un helper para campos string opcionales (Go
no tiene `Option<T>`; la opcionalidad se modela con punteros).

### `c.Sat`

```go theme={null}
c.Sat.ValidateRfc(ctx, &origoid.ValidateRfcRequest{Rfc: "PEZJ811011KI1"})

c.Sat.ExtractCsf(ctx, map[string]any{
    "rfc": "PEZJ811011KI1",
    "cif": "17060597619",
})

c.Sat.ValidateCfdi(ctx, map[string]any{
    "uuid":        "7C8BD4EA-AE86-4CB5-88B8-C6E61E988A8B",
    "rfcEmisor":   "PEZJ811011KI1",
    "rfcReceptor": "EMP170623KI3",
    "total":       "999999.99",
})
```

`ExtractCsf` y `ValidateCfdi` aceptan `map[string]any` porque los
schemas permiten shapes alternativas (identificadores directos O
upload de documento).

### `c.Imss`

```go theme={null}
c.Imss.LookupNss(ctx, &origoid.LookupNssRequest{Curp: "PELJ900101HDFRRN09"})

c.Imss.GetEmploymentStatus(ctx, &origoid.GetEmploymentStatusRequest{
    Curp: "PELJ900101HDFRRN09",
    Nss:  "92038109713",
})
```

### `c.Ine`

```go theme={null}
c.Ine.ValidateVoterList(ctx, map[string]any{
    "cic":               "123456789",
    "citizenIdentifier": "987654321",
})

c.Ine.ExtractVoterIdData(ctx, &origoid.ExtractVoterIdDataRequest{
    Front: "<jpg base64>",
    Back:  origoid.String("<jpg base64>"),
})

c.Ine.ExtractQrData(ctx, &origoid.ExtractQrDataRequest{
    Back: "<jpg base64 del reverso>",
})
```

### `c.Compliance`

```go theme={null}
c.Compliance.SearchSat69(ctx, map[string]any{"rfc": "PEZJ811011KI1"})
c.Compliance.SearchSat69B(ctx, map[string]any{"rfc": "PEZJ811011KI1"})

c.Compliance.SearchOfac(ctx, &origoid.SearchOfacRequest{
    Name:               "John Doe",
    MinSimilarityScore: origoid.Int(85),
})

c.Compliance.SearchPeps(ctx, map[string]any{
    "givenNames":    "JUAN",
    "firstSurname":  "PEREZ",
    "secondSurname": "LOPEZ",
})
```

### `c.Biometrics`

```go theme={null}
c.Biometrics.MatchFaces(ctx, &origoid.MatchFacesRequest{
    Face:         "<selfie base64>",
    Front:        "<frente id base64>",
    Threshold:    origoid.Int(80),
    DocumentType: origoid.MatchFacesRequestDocumentTypeIne.Ptr(),
})

c.Biometrics.CheckLiveness(ctx, &origoid.CheckLivenessRequest{
    Selfie: "<selfie base64>",
})
```

### `c.Email`

```go theme={null}
c.Email.ValidateEmail(ctx, &origoid.ValidateEmailRequest{
    Email: "user@example.com",
})
```

### `c.ProofOfAddress`

```go theme={null}
c.ProofOfAddress.ExtractProofOfAddress(ctx, &origoid.ExtractProofOfAddressRequest{
    File: "<pdf o jpg base64>",
})
```

## Manejo de errores

El SDK distingue entre **errores de negocio** (vienen dentro del
envelope) y **errores de transporte** (devueltos como `error` no
nulo).

### Errores de negocio — inspecciona el envelope

Para cualquier HTTP 200, incluyendo `INVALID_REQUEST`, el SDK
devuelve un `*Envelope` normal. Revisa `Status` y `Type` antes de
usar `Data`:

```go theme={null}
env, err := c.Renapo.ValidateCurp(ctx, &origoid.ValidateCurpRequest{Curp: "BAD"})
if err != nil {
    log.Fatal(err)
}

switch env.Type {
case "SUCCESS":
    // env.Data trae el registro
case "CURP_NOT_FOUND":
    // sin match
case "INVALID_REQUEST":
    for _, e := range env.Errors {
        fmt.Printf("  %s: %s — %s\n", e.Field, e.Code, e.Message)
    }
}
```

### Errores de transporte — `error` no nulo

Para `401`, `429`, fallas de red, el SDK devuelve un error tipado:

```go theme={null}
import "errors"

env, err := c.Renapo.ValidateCurp(ctx, req)
if err != nil {
    var unauth *origoid.UnauthorizedError
    var rl *origoid.TooManyRequestsError
    switch {
    case errors.As(err, &unauth):
        // 401
    case errors.As(err, &rl):
        // 429 — lee rl.Body para el envelope de rate-limit
    default:
        // red / timeout / inesperado
        log.Fatal(err)
    }
}
```

## Configuración por llamada (avanzado)

Usa helpers `option.With*` como argumentos adicionales:

```go theme={null}
import "time"

env, err := c.Ine.ValidateVoterList(
    ctx,
    map[string]any{"cic": "123456789", "citizenIdentifier": "987654321"},
    option.WithRequestTimeout(120 * time.Second),
    option.WithMaxAttempts(3),
)
```

Pasa un `context.Context` para respetar deadlines o cancelaciones del
caller; el SDK respeta `ctx.Done()` y aborta requests en vuelo.

<Info>
  **Lee esto antes de tunear timeouts o retries.** El SDK sólo
  reintenta errores `5xx` y fallas de red, nunca respuestas de negocio
  exitosas — entonces los retries **no** crean llamadas duplicadas
  facturables cuando el API respondió correctamente. **Sí** crean
  llamadas extra cuando el request realmente falló: un request que
  hace timeout tres veces puede consumir tres créditos si la llamada
  eventualmente tuvo éxito en un intento posterior.

  * **Los defaults (60 s, 2 reintentos) son correctos para casi
    cualquier workload.** Cambia sólo con razón específica.
  * Combinar timeout largo con `MaxAttempts` alto (ej. 120 s × 5)
    significa que un único request fallando puede ocupar una
    goroutine hasta 10 minutos — malo para tu throughput y tu
    infraestructura.
  * Sobrescribe per-call sólo en endpoints con cold starts lentos
    conocidos.
</Info>

## Punteros para campos opcionales

Go no tiene `Option<T>`, así que los campos opcionales en structs de
request son tipos puntero. El SDK expone helpers para hacerlo menos
verboso:

```go theme={null}
origoid.String("valor opcional")  // *string
origoid.Int(42)                   // *int
origoid.Bool(true)                // *bool
```

Para enums:

```go theme={null}
origoid.MatchFacesRequestDocumentTypeIne.Ptr()
```
