Skip to main content
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

go get github.com/origoid/sdk-go@v0.1.0
Or add it to go.mod:
require github.com/origoid/sdk-go v0.1.0
Then run go mod tidy. Source on github.com/origoid/sdk-go (public, for auditing). Requires Go 1.21 or newer.

Initialize the client

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, 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.
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 for the contract.

Methods by resource

The client groups operations by regulatory domain.

c.Authentication

c.Authentication.IssueToken(ctx, &origoid.IssueTokenRequest{ExpireAfter: 1800})

c.Renapo

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

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

c.Imss.LookupNss(ctx, &origoid.LookupNssRequest{Curp: "PELJ900101HDFRRN09"})

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

c.Ine

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

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

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

c.Email.ValidateEmail(ctx, &origoid.ValidateEmailRequest{
    Email: "user@example.com",
})

c.ProofOfAddress

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

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:
origoid.String("optional value")  // *string
origoid.Int(42)                   // *int
origoid.Bool(true)                // *bool
For enums:
origoid.MatchFacesRequestDocumentTypeIne.Ptr()