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

# Go SDK

> Official Go client. Context-aware, idiomatic, distributed via Go modules.

The Go SDK is distributed as a standard Go module. There is no
separate registry — `go get` fetches the source directly from GitHub.
The SDK provides one strongly-typed function per OrigoID endpoint and
follows idiomatic Go patterns (`context.Context`, exported structs,
explicit error returns).

## Install

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

Or add it to `go.mod`:

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

Then run `go mod tidy`. Source on
[github.com/origoid/sdk-go](https://github.com/origoid/sdk-go) (public,
for auditing). Requires Go **1.21 or newer**.

## Initialize the client

```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")
}
```

That's the whole setup — there is nothing else to configure. Never
hardcode the key. Load it from `os.Getenv`, [`viper`](https://github.com/spf13/viper),
or a secrets manager (HashiCorp Vault, 1Password, Doppler, etc.).

## Your first call

The example below uses `PELJ900101HDFRRN09`, a **synthetic CURP from
the OpenAPI examples** — not a real person's CURP. Replace it with the
CURP you need to validate.

```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", // synthetic — replace with real input
        },
    )
    if err != nil {
        log.Fatal(err)
    }

    if env.Status == "OK" && env.Type == "SUCCESS" {
        // env.Data holds the RENAPO record.
        fmt.Println("CURP holder:", env.Data)
    } else {
        // Other result type — check the endpoint's response catalog to drive your logic.
        fmt.Println(env.Type, "—", env.Message)
    }
}
```

Every method returns `(*origoid.Envelope, error)`. The envelope shape
is `{ Status, Type, Message, Data, TransactionId, ProcessedAt,
Billable, Errors }`. See [Response envelope](/en/envelope) for the
contract.

## Methods by resource

The client groups operations by regulatory domain.

### `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(...)` is a helper for optional string fields (Go has
no `Option<T>`; optionality is modeled with pointers).

### `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` and `ValidateCfdi` accept a `map[string]any` because the
underlying schemas allow alternative shapes (direct identifiers OR a
document upload).

### `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: "<base64 jpg>",
    Back:  origoid.String("<base64 jpg>"),
})

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

### `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:         "<base64 selfie>",
    Front:        "<base64 id front>",
    Threshold:    origoid.Int(80),
    DocumentType: origoid.MatchFacesRequestDocumentTypeIne.Ptr(),
})

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

### `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: "<base64 pdf or jpg>",
})
```

## Error handling

The SDK distinguishes between **business errors** (returned inside the
envelope) and **transport errors** (returned as a non-nil `error`).

### Business errors — inspect the envelope

For any HTTP 200, including `INVALID_REQUEST`, the SDK returns a
`*Envelope`. Check `Status` and `Type` before using `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 holds the record
case "CURP_NOT_FOUND":
    // no match
case "INVALID_REQUEST":
    for _, e := range env.Errors {
        fmt.Printf("  %s: %s — %s\n", e.Field, e.Code, e.Message)
    }
}
```

### Transport errors — non-nil `error`

For `401`, `429`, network failures, the SDK returns a typed error:

```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 — read rl.Body for the rate-limit envelope
    default:
        // network / timeout / unexpected
        log.Fatal(err)
    }
}
```

## Per-call configuration (advanced)

Use `option.With*` helpers as additional arguments:

```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),
)
```

Pass a `context.Context` to honor deadlines or cancellations from the
caller; the SDK respects `ctx.Done()` and aborts in-flight requests.

<Info>
  **Read this before tuning timeouts or retries.** The SDK only
  retries `5xx` and network failures, never successful business
  responses — so retries do **not** create duplicate billable calls
  when the API responded correctly. They **do** create extra calls
  when the request actually failed: a request that times out three
  times can consume three credits if the call eventually succeeded on
  a later attempt.

  * **Defaults (60 s, 2 retries) are right for almost every workload.**
    Change only with a specific reason.
  * Combining a long timeout with high `MaxAttempts` (e.g. 120 s × 5)
    means a single failing request can occupy a goroutine for up to 10
    minutes — bad for your throughput and your infrastructure.
  * Override per-call only on endpoints with known slow cold starts.
</Info>

## Pointers for optional fields

Go has no `Option<T>`, so optional fields in request structs are
pointer types. The SDK exposes helper constructors to make this less
verbose:

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

For enums:

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