Calling the API from Go
Pass path ids as arguments and everything else in one *alertops.Request; its Fields become the JSON body for POST/PUT/PATCH and query parameters otherwise, and failures are typed errors such as *alertops.NotFoundError.
Overview
Relevant for Developers writing calls against any endpoint
Every generated method has the shape Method(ctx, pathValues…, *alertops.Request) (any, error). Path values come first, in path order. The request carries everything else and follows the Python SDK's keyword convention, so a Python example translates one to one. Responses are schema-free JSON, because AlertOps response shapes vary by endpoint and sometimes by call:
- Objects are map[string]any.
- Top-level lists are alertops.AOList, with Items and Raw (the envelope the list came from).
- Nested lists are []any.
Read results with alertops.Get(v, "a", "b"), or decode them into your own struct with alertops.Decode(v, &target).
Path values and the request
Relevant for Developers building requests
A nil request is an empty one.
| Field | Description |
| Fields | Merged into the JSON body for POST, PUT, PATCH and body-carrying DELETEs. Sent as query parameters for every other method. |
| Body | An explicit body: any JSON value, a []any for bare-array bodies, or alertops.Null for an explicit null. Fields merge into it when it is a map. |
| Params | Extra query parameters, also for body methods. |
| Files | Multipart parts, map[string]alertops.File. The body's fields become form fields. |
| Headers | Extra request headers. |
ao.Groups.List(ctx, &alertops.Request{Fields: alertops.M{"search": "Network", "limit": 100}}) // GET ?search=…&limit=100
ao.Groups.Create(ctx, &alertops.Request{Fields: alertops.M{"group_name": "Network Ops"}}) // POST {"group_name": …}
ao.Groups.Members.Add(ctx, 11, &alertops.Request{Body: []any{alertops.M{"member_type": "User", "member": "7", "sequence": 1, "roles": []any{"Primary"}}}})
ao.EscalationPolicies.UpdateBasic(ctx, 3, &alertops.Request{Body: wholeRecord})
Go maps have no order
Python dicts keep insertion order, and in a few places that order is observable. Examples: attributes that a helper JSON-encodes into a string field, attribute rows, and a status page's group layout. Pass an alertops.OrderedMap there to control the order. Plain maps are written in key order.
Query values follow Python's rules:
- true and false become true/false.
- Slices are comma-joined.
- nil values are dropped.
- Numbers use Python's float formatting.
Path values are percent-encoded, so a name such as Email Official/2 is safe.
Pagination
Relevant for Developers reading long lists
Paginated endpoints have three methods: List (one page), ListAll (every page collected into a []any) and ListIter (an iter.Seq2[any, error] that fetches lazily). Break out of the loop to stop early.
every, err := ao.Groups.ListAll(ctx, &alertops.Request{Fields: alertops.M{"search": "sdk"}})
for alert, err := range ao.Alerts.ListIter(ctx, &alertops.Request{Fields: alertops.M{"createdFrom": "2026-03-01", "createdTo": "2026-03-08"}}) {
if err != nil {
return err
}
if alertops.Get(alert, "status") == "Open" {
fmt.Println(alertops.Get(alert, "alert_id"))
}
}
| Style | Used by | Behaviour |
| page index | groups, users, policies, audit trail … | limit + offset, where offset is a page index (0, 1, 2), not a row offset. Stops on a short page. |
| cursor | v3 lists | pageSize + after, read from nextCursor / hasMore. |
| page number | notification history | pageNumber + pageSize. |
| metadata cursor | alerts | limit + next, read from metadata.next. |
| limit only | inbound log | One page. |
Errors and retries
Relevant for Developers handling failures
Non-2xx answers return an error kind that wraps *alertops.APIError, so both errors.As(err, &nf) with nf *alertops.NotFoundError and errors.As(err, &api) with api *alertops.APIError work. An APIError carries:
- Status, Body, Method and URL.
- Op: the Python op id, such as Groups.get.
- OpID: the dotted id, such as groups.get.
- Messages: normalised from the three AlertOps error shapes.
| Error | When |
| *BadRequestError | 400 or 422 |
| *AuthenticationError | 401 |
| *PaymentRequiredError | 402, the feature is not in the plan |
| *PermissionDeniedError | 403 |
| *NotFoundError | 404 (alertops.IsNotFound(err)) |
| *ConflictError | 409 |
| *RateLimitError | 429 after retries |
| *ServerError | 5xx |
| *TransportError | network failure after retries |
| *ValueError / *LookupError / *TypeError | a helper rejected an argument, or a named thing (role, zone, type) does not exist |
Retries are automatic:
- Which calls:
- GET, PUT and DELETE retry on 429, 502, 503 and 504.
- POST retries only on 429 and on connect failures, so a create is never sent twice.
- Delay: BackoffFactor × 2^attempt (default 500 ms, capped at 8 s, with ±25 % jitter).
- Retry-After: honoured when the server sends it, up to 32 s.
Cancelling ctx stops a call and its retries.
Listeners and the escape hatch
Relevant for Developers adding logging or calling unmodelled paths
ao.AddListener(func(e alertops.CallEvent) { log.Println(e.OpID, e.Method, e.URL, e.Status, e.Elapsed) })
res, err := ao.Request(ctx, "GET", "/groups", &alertops.RequestOptions{Base: "v2", Request: alertops.Request{Fields: alertops.M{"search": "Net"}}})
Troubleshooting
Relevant for Anyone debugging a call
Groups.list() does not take a request body
- You set Body on a GET. Use Fields or Params instead.
A PUT wiped fields on the record
- Many AlertOps PUTs replace the whole record. Use the read-merge-write helpers (ao.EscalationPolicies.Update, ao.Schedules.UpdateFields), or GET the record, change it and PUT it back as Body.
Page 2 repeats page 1
- You set offset to a row count. Offset is a page index. Use ListAll or ListIter instead.
Related Articles
Need help?
Email support@alertops.com or contact your AlertOps representative.