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

# Go SDK

> Official Go SDK for the Alguna Public API: module, installation, client options, resources, pagination, errors, retries, and webhook types.

`github.com/alguna-io/alguna-go` is the official Go client for the Alguna Public API. It has one typed method for each endpoint: customers, products and plans, subscriptions, usage events, credits and wallets, invoices, payments, and checkout and portal sessions. The request and response types come from the versioned OpenAPI spec. Use it from Go services where you would otherwise write HTTP calls by hand. It sets the `Authorization` and `Alguna-Version` headers, returns typed errors, sends an idempotency key on writes, and retries failed calls only when a retry is safe. The objects it handles are described in [How Alguna works](/docs/concepts/how-alguna-works).

<Note>
  Use the SDK on a server only. API keys are secret: never ship them to a browser or a mobile app. To embed a purchase flow in your frontend, use [Embedded Checkout](/docs/hosted/embedded-checkout). Your server creates the session with this SDK, and your frontend mounts it.
</Note>

***

## How it works

1. **Create an API key** under **Settings → Connections → Developers → API keys**. A key looks like `<key_id>.<key_secret>`.
2. **Create the client** with `client.NewAlguna`. With no options, it reads the key from `ALGUNA_API_KEY`. Every request carries `Authorization: Bearer` and the `Alguna-Version` header.
3. **Call resources** such as `c.Customers` and `c.Subscriptions`. Every method takes a `context.Context` first, and returns a typed response and an `error`.

## Installation

```sh theme={null}
go get github.com/alguna-io/alguna-go
```

The SDK needs Go 1.21 or later. The module is [`github.com/alguna-io/alguna-go` on pkg.go.dev](https://pkg.go.dev/github.com/alguna-io/alguna-go). Its source is [alguna-io/alguna-go](https://github.com/alguna-io/alguna-go) on GitHub. A release is a git tag on that repository.

```go theme={null}
import (
	alguna "github.com/alguna-io/alguna-go"
	"github.com/alguna-io/alguna-go/client"
	"github.com/alguna-io/alguna-go/option"
)

// With no options, the client reads the API key from ALGUNA_API_KEY.
c := client.NewAlguna(
	option.WithAPIVersion("2026-04-01"), // recommended: pin the version you integrate against
)
```

The request and response types are in the root `alguna` package. The options are in `option`.

***

## Quick start: customer → subscription → invoice → portal session

Optional fields are pointers. `alguna.String`, `alguna.Int`, `alguna.Bool` and the other helpers in the root package return a pointer to a value.

```go theme={null}
ctx := context.Background()

// 1. Create a customer
customer, err := c.Customers.Create(ctx, &alguna.CreateCustomerRequest{
	Name:     "Acme Corp",
	Currency: "USD",
	Contacts: []*alguna.ContactRequest{
		{Email: "billing@acme.com", IsPrimary: true},
	},
})
if err != nil {
	return err
}

// 2. Create a product and start a subscription
product, err := c.Products.Create(ctx, &alguna.CreateProductRequest{
	Name:             "Platform Fee",
	FeeType:          alguna.PricingClassFixed,
	BillingFrequency: alguna.BillingFrequencyRecurring.Ptr(),
	PaymentTerms:     alguna.BillingDirectionAdvance,
})
if err != nil {
	return err
}

subscription, err := c.Subscriptions.Create(ctx, &alguna.CreateSubscriptionRequest{
	CustomerID: customer.ID,
	Currency:   alguna.String("USD"),
	Items: []*alguna.CreateSubscriptionItemInput{
		{
			ProductID: alguna.String(product.ID),
			Price: &alguna.PriceInput{
				Type:              alguna.PricingTypeFixed,
				FeeType:           alguna.PricingClassFixed,
				BillingDirection:  alguna.BillingDirectionAdvance,
				BillingFrequency:  alguna.BillingFrequencyRecurring.Ptr(),
				BillingInterval:   alguna.BillingIntervalMonthly.Ptr(),
				FixedPricingModel: &alguna.FixedPricingModelInput{PricePerUnit: "99.00", Units: 1},
			},
		},
	},
	Contract: &alguna.ContractInput{
		PeriodType:     alguna.String("fixed"),
		DurationMonths: alguna.Int64(12),
		StartDate:      alguna.Time(time.Date(2026, 7, 1, 0, 0, 0, 0, time.UTC)),
	},
	AutoActivate: alguna.Bool(true),
})
if err != nil {
	return err
}
fmt.Println(subscription.ID)

// 3. Issue a one-off invoice
invoice, err := c.Invoices.Create(ctx, &alguna.CreateInvoiceRequest{
	CustomerID: alguna.String(customer.ID),
	Currency:   alguna.String("USD"),
	LineItems: []*alguna.CreateInvoiceLineItemRequest{
		{Description: alguna.String("Onboarding"), UnitPrice: "500.00", Quantity: "1"},
	},
})
if err != nil {
	return err
}
fmt.Println(invoice.ID)

// 4. Open a customer portal session so the customer can pay and manage billing
session, err := c.Portal.CreateSession(ctx, &alguna.CreateCustomerPortalSessionRequest{
	CustomerID: alguna.String(customer.ID),
})
if err != nil {
	return err
}
fmt.Println(session.URL) // send the customer here
```

<Note>
  Subscriptions are created in `draft` status. Set `AutoActivate` (as above) or call `c.Subscriptions.Activate` when you are ready to go live. See [Creating a Subscription](/docs/api-reference/v2/examples/creating-a-subscription) for the full set of patterns.
</Note>

***

## Resources

The client has one field for each API resource, and one method for each operation. The client is generated from the versioned spec, so every public operation has a method.

| Resource | Methods |
| - | - |
| `c.Customers` | `Create`, `Get`, `List`, `Update`, entitlements, connected accounts |
| `c.Subscriptions` | `Create`, `Get`, `List`, `Update`, `Activate`, credits, entitlements, revenue, `DownloadPdf` |
| `c.SubscriptionVersions` | subscription versions |
| `c.SubscriptionChanges` | changes to an active subscription |
| `c.Invoices` | `Create`, `Get`, `List`, `Update`, `MarkAsPaid`, `Void`, `GeneratePdf`, line items |
| `c.Portal` / `c.CheckoutSessions` | `CreateSession` for the [customer portal](/docs/hosted/customer-portal); [checkout sessions](/docs/hosted/checkout) |
| `c.Events` / `c.Metrics` | `Ingest` usage events; [billable metrics](/docs/billable-metrics/define-metrics) |
| `c.Credits` / `c.Wallets` / `c.WalletGrants` | `Check`, `Track`; wallets and grants. See [Credits & wallets](/docs/credits/overview) |

The client also has `Products`, `Plans`, `Bundles`, `CreditNotes`, `Payments`, `Refunds`, `Integrations`, `Insights`, `Quotes`, `RevenueSchedules`, `Merchants`, `Tags`, `Tax`, `Users` and `Feedback`. Method names follow the operation they wrap. The full list is in [`reference.md`](https://github.com/alguna-io/alguna-go/blob/main/reference.md) in the SDK repository, and in the generated [API reference](/docs/api-reference/v2/overview).

Every method takes per-call options as its last arguments: `option.WithIdempotencyKey` (writes), `option.WithMaxAttempts`, `option.WithoutRetries`, `option.WithHTTPClient`, `option.WithHTTPHeader` and `option.WithAPIVersion`.

To get the status code and headers as well, call the same method on `WithRawResponse`, for example `c.Customers.WithRawResponse.Get(ctx, id)`.

## Pagination

List methods accept `Limit`, `Offset` and the same filter parameters as the [HTTP API](/docs/api-reference/v2/overview#filtering-and-sorting). A list method returns a `*core.Page`: `Results` holds the items of the current page, and `Response` holds the full body. `Iterator` walks every item and gets the next page when it needs one.

```go theme={null}
page, err := c.Customers.List(ctx, &alguna.ListCustomersRequest{Limit: alguna.Int(100)})
if err != nil {
	return err
}
iter := page.Iterator()
for iter.Next(ctx) {
	fmt.Println(iter.Current().ID)
}
if err := iter.Err(); err != nil {
	return err
}
```

The API sends at most the operation's `limit` maximum per page. The next page starts after the items that the SDK got, so a larger `Limit` skips nothing. Iteration stops at the first empty page, so a walk ends with one request that returns no items. To page by hand, call `page.GetNextPage(ctx)`. After the last page, it returns `core.ErrNoPages`.

## PDF downloads

`Invoices.GeneratePdf`, `Quotes.GetPdf` and `Subscriptions.DownloadPdf` return an `io.Reader`:

```go theme={null}
pdf, err := c.Invoices.GeneratePdf(ctx, invoice.ID)
if err != nil {
	return err
}
file, err := os.Create("invoice.pdf")
if err != nil {
	return err
}
defer file.Close()
if _, err := io.Copy(file, pdf); err != nil {
	return err
}
```

***

## Error handling

When the API answers with an error status, the call returns one of the typed errors below. Each type embeds `*core.APIError`, which has `StatusCode` and `Header`. Match them with `errors.As`:

```go theme={null}
_, err := c.Customers.Get(ctx, "cus_missing")
var notFound *alguna.NotFoundError
var apiErr *core.APIError
switch {
case errors.As(err, &notFound):
	fmt.Println("no such customer:", notFound.Body.Detail)
case errors.As(err, &apiErr):
	fmt.Println(apiErr.StatusCode, apiErr.Header.Get("X-Request-Id"), err)
case err != nil:
	return err
}
```

`core` is `github.com/alguna-io/alguna-go/core`.

| Error | Status |
| - | - |
| `*alguna.BadRequestError` | 400 |
| `*alguna.UnauthorizedError` | 401 |
| `*alguna.ForbiddenError` | 403 |
| `*alguna.NotFoundError` | 404 |
| `*alguna.ConflictError` | 409 |
| `*alguna.UnprocessableEntityError` | 422 |
| `*alguna.TooManyRequestsError` | 429 |
| `*alguna.InternalServerError` | 500 |
| `*alguna.BadGatewayError` | 502 |
| `*alguna.ServiceUnavailableError` | 503 |
| `*alguna.GatewayTimeoutError` | 504 |
| `*core.APIError` | any other error status |

`err.Error()` is the status and the body's `detail`, else its `title`, else its `message`, else the HTTP status text. For example: `404: customer not found`. Each typed error has the decoded body in `Body`. When the body does not decode, as with the empty body of some `502` responses, the error is a plain `*core.APIError`. A failure with no response is the `*url.Error` from `net/http`.

Include the `X-Request-Id` header of the failed response when you contact support. Status meanings and retry guidance are on the [Errors](/docs/api-reference/v2/errors) page.

***

## Idempotency and retries

Every request that is not a GET sends an `Idempotency-Key`: your key, or a UUID v4 that the SDK makes for the call. Every retry of the call sends the same key. The API then returns the first result and does not repeat the write (see [Idempotency](/docs/api-reference/v2/idempotency)). Pass your own key when you repeat a call yourself, for example after a restart:

```go theme={null}
created, err := c.Customers.Create(ctx,
	&alguna.CreateCustomerRequest{Name: "Acme Corp", Currency: "USD"},
	option.WithIdempotencyKey(alguna.String("signup-1234")),
)
if err != nil {
	return err
}
fmt.Println(created.ID)
```

`Events.Ingest`, `Credits.Check` and `Credits.Track` are different. The events in the request body identify the work, so the SDK makes no key for these calls and still retries them. A key that you pass is sent on every attempt. See [Writes identified by their events](/docs/api-reference/v2/idempotency#writes-identified-by-their-events).

The SDK retries a failed attempt 2 times by default (3 attempts in total) when:

* the status is `408`, `429`, `502`, `503` or `504`;
* the status is `409` with a `Retry-After` header. The API sends this only while a request with the same key is still running. A `409` without `Retry-After` is a real conflict and is not retried;
* no response arrived: the connection was refused, reset or closed, or the attempt timed out.

The SDK does not retry after `ctx` is cancelled or its deadline passes. Between attempts it waits 1 s, then 2 s, with random jitter of up to 10%. When the response has a `Retry-After` header, it waits that long instead, at most 60 s. `option.WithMaxAttempts(n)` sets the number of attempts, the first included, on the client or on one call. `option.WithMaxAttempts(0)` means the default of 3 attempts. To turn retries off, use `option.WithoutRetries()`.

Each attempt times out after 60 s. A timed-out attempt is retried, so one call can take 3 times the timeout plus the waits. To set a different time for each attempt, pass your own `http.Client` with a `Timeout`. To limit the whole call, retries included, give `ctx` a deadline:

```go theme={null}
c := client.NewAlguna(option.WithHTTPClient(&http.Client{Timeout: 10 * time.Second}))

// Each attempt may take 10 s; the call, with its retries, at most 25 s.
ctx, cancel := context.WithTimeout(ctx, 25*time.Second)
defer cancel()
customer, err := c.Customers.Get(ctx, "cus_123")
if err != nil {
	return err
}
fmt.Println(customer.ID)
```

***

## Configuration

```go theme={null}
c := client.NewAlguna(
	option.WithAPIKey(os.Getenv("ALGUNA_SANDBOX_API_KEY")),    // default: ALGUNA_API_KEY
	option.WithAPIVersion("2026-04-01"),                       // default: the version this SDK release targets
	option.WithBaseURL("https://api.sandbox.alguna.io"),       // default: https://api.alguna.io
	option.WithHTTPHeader(http.Header{"X-Trace-Id": {"abc"}}), // sent on every request
	option.WithMaxAttempts(3),                                 // see Idempotency and retries
)
```

`client.NewAlguna` cannot return an error, so it does not check the key. A client without a key sends each request anyway, and the API answers with a `401`: the call returns an `*alguna.UnauthorizedError`.

Pin the API version with `option.WithAPIVersion`. Without it, the SDK sends the version its types were generated from, so upgrading the module can move you to a newer API version. See [Versioning](/docs/api-reference/v2/overview#versioning).

The SDK sends `User-Agent: alguna-go/<version>`. A `User-Agent` in `option.WithHTTPHeader` replaces it.

## Webhook types

The `alguna` package has one type for each webhook body, such as `alguna.WebhookPaymentCreatedPayload`. Read `type` first, then decode the body into the matching type. The SDK types a payload. It does not check that Alguna sent it. Verify the `svix-id`, `svix-timestamp` and `svix-signature` headers against the raw request body before you trust a payload, as [Verifying signatures](/docs/api-reference/v2/webhooks#verifying-signatures) shows.

***

## FAQ

**Can I use the SDK in a browser or mobile app?**
No. It authenticates with your secret API key. For browser purchase flows, create a checkout session on your server and mount it with [Embedded Checkout](/docs/hosted/embedded-checkout).

**Does the SDK cover every endpoint?**
Yes. The client is generated from the versioned OpenAPI spec, so every operation in the [API reference](/docs/api-reference/v2/overview) has a method.

**Why are some fields pointers?**
Optional fields are pointers, so that an unset field is not sent. Use `alguna.String`, `alguna.Int`, `alguna.Bool` and the other helpers to set them.

**Which version am I on, and how does it relate to the API version?**
They are different. The module version is the git tag you installed. Read it from `go.mod`. The API version is the date sent as `Alguna-Version`, and it decides request and response shapes. When you pass `option.WithAPIVersion`, a module upgrade does not change it. Change it on purpose, as its own change, following [Versioning](/docs/api-reference/v2/overview#versioning).

**How do I point it at the sandbox?**
Pass `option.WithBaseURL("https://api.sandbox.alguna.io")` with a sandbox API key. Sandbox and production are separate environments with separate keys. See [Base URLs](/docs/api-reference/v2/overview#base-urls).

**Is there an SDK for another language?**
Yes. There are TypeScript, Python and Rust SDKs and an `alguna` CLI. They are generated from the same API spec. See [SDKs and CLI](/docs/api-reference/v2/sdks/overview).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.