Shared client for the Slack Admin API: form-encoded requests with the
admin user token, ok: false and rate-limit handling, and cursor
pagination.
Per-call credentials. Every API-calling function in this package
takes an optional trailing opts Map, resolved by resolve: opts
first, then the slack.admin.token context variable. Nothing from
opts is written to ctx, so one run can act for many organizations and
parallel flows don't share credentials. A token from opts is masked
in call records. A token present but blank or null is an error, never
a fallback to slack.admin.token.
token: the org-level admin user token for this call
Context Vars
slack.admin.token
Functions
build-request
fn (method: Str, request: Any): ::hot::http/HttpRequest
fn (method: Str, request: Any, token: Str?): ::hot::http/HttpRequest
fn (method: Str, request: Any, opts: Map): ::hot::http/HttpRequest
Build (without sending) the POST to https://slack.com/api/<method>
with a form body and Authorization: Bearer <token>. The 3rd argument
is per-call opts (see resolve), or an explicit token instead of
slack.admin.token. Returns an Err when the call has no usable token.
call
fn (method: Str, request: Any): Map
fn (method: Str, request: Any, token: Str?): Map
fn (method: Str, request: Any, opts: Map): Map
Call any Slack admin method with a request record or Map and return
the response body, or an Err(SlackError). Use it for methods this
package doesn't wrap. The 3rd argument is per-call opts (see
resolve) or an explicit token.
Example
teams ::slack::admin::api/call("admin.teams.list", {limit: 100})
teams.teams
tenant ::slack::admin::api/call("admin.teams.list", {limit: 100}, {token: "xoxp-tenant-b"})
collect-pages
fn (fetch: Fn, params: Map, items-key: Str, max-items: Int, acc: Vec): Vec
The paging loop behind paginate, with the page fetcher passed in:
fetch takes the params Map and returns a response body. A failed page
comes back as its Err. Exposed for testing.
form-body
fn (request: Any): Str
Encode a request record as application/x-www-form-urlencoded,
dropping unset and null fields. Exposed for testing.
Example
::slack::admin::api/form-body({channel_ids: ["C1", "C2"], cursor: null, limit: 50})
// => "channel_ids=C1%2CC2&limit=50"
form-value
fn (value: Any): Str
Encode one argument the way Slack's admin methods expect in a form
body: lists of strings comma-separated (C1,C2), other lists and maps
as JSON strings (e.g. prefs), booleans as true/false.
is-rate-limited
fn (error: Any): Bool
True when error is a SlackError for HTTP 429; wait error.retry_after seconds before retrying.
next-cursor
fn (body: Map): Str?
The next page cursor from a response (response_metadata.next_cursor, or a top-level next_cursor), or null on the last page.
non-blank
fn (value: Any): Str?
paginate
fn (method: Str, request: Any, items-key: Str, max-items: Int): Vec
fn (method: Str, request: Any, items-key: Str, max-items: Int, opts: Map): Vec
Call a cursor-paginated admin method repeatedly, following
next_cursor, and return the concatenated items-key lists (up to
max-items). The per-module *-all helpers use it.
Example
users ::slack::admin::api/paginate("admin.users.list", {team_id: "T0123ABCDEF", limit: 200}, "users", 5000)
parse-response
fn (method: Str, response: Result): Any
Return the body of a successful Slack response, or an Err(SlackError)
for HTTP errors, rate limits (429), and ok: false bodies (Slack's
usual failure, sent with HTTP 200). Exposed for testing.
resolve
fn (opts: Map): Map
The admin token for one call (see resolve-from). A token from
opts is masked in call records with ::hot::ctx/mark-secret, and
opts values are never written to ctx.
Example
::slack::admin::api/resolve({token: "xoxp-tenant-b"}).token
slack.admin.token
resolve-from
fn (opts: Map, ctx-values: Map): Map
The one resolver, pure: {token} for a call from opts first, then ctx-values (a Map of context variables). An Err when no token is set, or when token is in opts but blank or null (that never falls back to context). Exposed for testing; resolve feeds it the context.
slack-error
fn (method: Str, response: ::hot::http/HttpResponse): SlackError
Build the SlackError for a failed response to method. Exposed for testing; parse-response uses it.
Types
SlackError
SlackError type {
method: Str,
error: Str,
status: Int,
messages: Vec<Str>?,
warning: Str?,
needed: Str?,
provided: Str?,
retry_after: Int?,
body: Any
}
A failed admin call. error is Slack's error code (not_allowed_token_type,
missing_scope, channel_not_found, ...); messages holds
response_metadata.messages (per-argument detail for
invalid_arguments); needed/provided list scopes on
missing_scope. HTTP-level failures use http_<status>, and HTTP 429
uses ratelimited with retry_after seconds from Retry-After.
Example
result ::slack::admin::conversations/archive(::slack::admin::conversations/ArchiveRequest({channel_id: "C0123ABCDEF"}))
if-err(result, (e) { `archive failed: ${e.error} ${join(or(e.messages, []), "; ")}` })