Go SDK
Build a maxclicks integration into your Go service. Sync customer data, fire events, and trigger prepared experiences with context-aware requests and typed results.
The module resolves from its source repository as an untagged Go pseudo-version. Pin the version you test with your application. You can also follow the HTTP quickstart to call the API directly.
Current API coverage
This guide documents the existing source client interface. The current REST API also exposes event readiness, event identity changes, record deletion status, and workflow history cursors that this client does not yet wrap. Event receipts and deletion results have changed; older static models may omit fields or expect the former { id, deleted } shape.
Use direct HTTP for those operations and verify the current coverage table before adopting a wrapper. Auto-pagination still stops at the API's traversal cap; an iterator or method named all does not guarantee a full workspace export.
Install
go get github.com/maxclicks-ai/maxclicks-go
Quickstart
The client reads MAXCLICKS_API_KEY from the environment, or pass a key with WithAPIKey. The key is not validated at construction: a missing key surfaces as *maxclicks.ConfigurationError on the first authenticated call.
package main
import (
"context"
"fmt"
"log"
"github.com/maxclicks-ai/maxclicks-go"
)
func main() {
client := maxclicks.New(maxclicks.WithAPIKey("max_..."))
ctx := context.Background()
me, err := client.Me(ctx)
if err != nil {
log.Fatal(err)
}
fmt.Println("space:", me.Space.Slug)
contact, err := client.Records.Upsert(ctx, "students", maxclicks.RecordInput{
"email": "[email protected]",
"firstName": "Ada",
"tags": []string{"vip"},
})
if err != nil {
log.Fatal(err)
}
fmt.Println("contact id:", contact.ID)
}
Configuration
maxclicks.New accepts functional options.
| Option | Default | Description |
|---|---|---|
WithAPIKey(string) | MAXCLICKS_API_KEY env var | Bearer API key. |
WithBaseURL(string) | https://api.maxclicks.ai/v1 | API base URL. Use maxclicks.StageBaseURL for stage. |
WithTimeout(time.Duration) | 60s | Per-request timeout. |
WithMaxRetries(int) | 2 | Extra retry attempts on retryable failures. |
WithHTTPClient(*http.Client) | &http.Client{} | Inject a custom client or transport. |
WithDefaultHeader(k, v string) | none | Add a header to every request. |
WithDefaultHeaders(map[string]string) | none | Add multiple headers. |
WithOnWarning(func(warnings []string, method, path string)) | none | Callback for API warnings. |
Errors
Every failure is a typed error. HTTP error responses map by status. Use errors.As against a specific type, or maxclicks.AsAPIError against the base *maxclicks.APIError.
_, err := client.Schemas.Get(ctx, "does-not-exist")
var notFound *maxclicks.NotFoundError
if errors.As(err, ¬Found) {
fmt.Println("missing:", notFound.Message, notFound.Code)
}
if apiErr, ok := maxclicks.AsAPIError(err); ok {
fmt.Println(apiErr.Status, apiErr.Type, apiErr.Code, apiErr.Message)
}
var rateLimit *maxclicks.RateLimitError
if errors.As(err, &rateLimit) {
time.Sleep(rateLimit.RetryAfter)
}
| Status or condition | Error type |
|---|---|
| 400 | *BadRequestError |
| 401 | *AuthenticationError |
| 402 | *PaymentRequiredError |
| 403 | *PermissionError |
| 404 | *NotFoundError |
| 405 | *MethodNotAllowedError |
| 409 | *ConflictError |
| 413 | *PayloadTooLargeError |
| 415 | *UnsupportedMediaTypeError |
| 422 | *UnprocessableEntityError |
| 429 | *RateLimitError (has RetryAfter) |
| 5xx | *ServerError |
| transport failure | *ConnectionError |
| timeout or deadline | *TimeoutError |
| missing API key | *ConfigurationError |
Every HTTP error type embeds *APIError, which carries Status, Code, Type, Message, Issues, Header, and RawBody. Issues is populated for validation-gate failures (such as broadcasts.send's broadcast_invalid) that report several problems at once. 402 signals insufficient_credits (an AI-generation or auto-fill call) or email_limit_reached (a send past the email allowance).
Retries are limited to what is safe to repeat. Reads retry on 429, any 5xx, and transport or timeout errors, up to WithMaxRetries extra attempts. A write retries on those only when you pass WithIdempotencyKey, since the server then collapses the repeat into the original; a keyless write that times out or hits a 5xx is reported to you instead of being sent again, so your code can inspect the outcome. The client also retries 429 responses according to its configured retry budget. Backoff is exponential with full jitter, and a Retry-After header (seconds or HTTP date) sets the delay floor.
Pagination
List endpoints return a *maxclicks.Page[T] and a matching ...All iterator that walks every page (stopping when hasMore is false or the maximum offset is reached).
// Single page:
page, err := client.Records.List(ctx, "students", &maxclicks.ListOptions{Limit: 100})
for _, r := range page.Data {
fmt.Println(r.ID)
}
if page.HasNextPage() {
page, err = page.NextPage(ctx)
}
// Auto-pager over every record:
it := client.Records.ListAll(ctx, "students", nil)
for it.Next(ctx) {
fmt.Println(it.Value().ID)
}
if err := it.Err(); err != nil {
log.Fatal(err)
}
client.Events.List is cursor-paginated, not offset-paginated. It returns a *maxclicks.CursorPage[Event] and a matching ListAll iterator, reading ready events within the selected time window. HasNextPage, NextPage, and Iterator work the same way.
Records and events preserve unknown custom-attribute keys in an Extra map[string]any field alongside the typed base fields.
Idempotency
Mutating endpoints the API documents as idempotency-aware accept maxclicks.WithIdempotencyKey(...) as a trailing option. A retry with the same key replays the original result instead of executing twice.
key := uuid.NewString()
contact, err := client.Records.Upsert(ctx, "students",
maxclicks.RecordInput{"email": "[email protected]"},
maxclicks.WithIdempotencyKey(key),
)
This source client exposes a key on seven operations: Records.Create, Records.Upsert, Suppressions.BatchCreate, Suppressions.BatchDelete, Templates.Send, Broadcasts.Send, and Workflows.Trigger. Other methods do not expose this option. The current API supports additional keyed writes; use direct HTTP as described in current API coverage. Do not assume every update or deletion is automatically safe to replay.
Warnings
The API returns non-fatal warnings in the response body (warnings array), or, for the empty-body Workflows.Trigger response, in the maxclicks-Warning-Message header. Register a handler to observe them.
client := maxclicks.New(
maxclicks.WithAPIKey("max_..."),
maxclicks.WithOnWarning(func(warnings []string, method, path string) {
log.Printf("warning on %s %s: %v", method, path, warnings)
}),
)
*Page[T] also exposes the warnings for that page as page.Warnings.
Resource surface
Methods are grouped by resource on the client. Each list method also exposes a matching ...All auto-paging iterator.
- Name
client.Me- Type
- GET /me
- Description
The authenticated space and API key.
- Name
client.Schemas- Type
- /schemas
- Description
List,Get.
- Name
client.Attributes- Type
- /schemas/{schema}/attributes
- Description
List.
- Name
client.Records- Type
- /schemas/{schema}/records
- Description
List,Create,Upsert,Get,Update,Delete,AuditTrail.
- Name
client.Suppressions- Type
- /contacts/suppressions
- Description
List,Create,BatchCreate,BatchDelete,Delete. Admin key.
- Name
client.Events- Type
- /events
- Description
List(cursor),Fire,FireBatch.
- Name
client.Domains- Type
- /domains
- Description
List,Get.
- Name
client.Senders- Type
- /senders
- Description
List.
- Name
client.Topics- Type
- /topics
- Description
List,Get.
- Name
client.Segments- Type
- /segments
- Description
List,Create,Get,Delete,Count,ListContacts.
- Name
client.Broadcasts- Type
- /broadcasts
- Description
List,Get,Update,Send,ListRuns,GetMetrics.
- Name
client.Templates- Type
- /templates
- Description
List,Get,Send.
- Name
client.Webhooks- Type
- /webhooks
- Description
List,Create,Get,Update,Delete,RotateSecret.
- Name
client.Workflows- Type
- /workflows
- Description
List,Get,Trigger,Pause,Unpause,ListRuns,GetRun.
- Name
client.Forms- Type
- /forms/{formId}
- Description
Submit,ConfirmDoubleOptIn. No auth.
- Name
client.Emails- Type
- /emails/{emailId}
- Description
UnsubscribeOneClick. No auth.
Notable behaviors:
Events.FireBatchreturns a*BatchFireResulteven when the API responds422(a batch with failed items is a business result, not an error). Passmaxclicks.BatchOnErrorContinueormaxclicks.BatchOnErrorAbort.Templates.Sendis synchronous and returns a*TemplateSendResponse. A genuine send failure comes back asStatus: "failed"with a genericError. Pre-send validation and eligibility failures are typed errors.Workflows.Triggerreturns an empty body. Its path segment is a workflow-step reference, not a workflow id.Forms.ConfirmDoubleOptInandEmails.UnsubscribeOneClickfollow the browser redirect and return a*RedirectResultwith the final landing URL.
Create schemas, attributes, domains, senders, topics, templates and broadcasts in the maxclicks app, then use the supported API operations to read them or act on prepared work. Manage API keys and CSV imports in the app too. The client's method list and the current API reference define what can be changed or triggered from code; use direct HTTP when a newer operation is not wrapped by this client.
Rate limits
Per API key: 100 read requests per second and 25 write requests per second. Exceeding a limit returns 429. The SDK retries automatically, honoring Retry-After.
The module path is github.com/maxclicks-ai/maxclicks-go. The SDK is MIT
licensed.
Retry outcomes
A repeated write can return a recorded result, including an error. If the API reports idempotency_outcome_unknown, inspect the affected resource or run history before creating a new operation. Keep the original key and payload while you reconcile. See the idempotency guide for supported operations and retention.