authentication and REST
the base URL is https://sometimeschat.com/api/bots/v1. this API uses application credentials and explicit installation context, independently of human account sessions.
credentials and request context
send Authorization: Bot TOKEN over HTTPS. all installation-resource routes require X-Sometimes-Installation. the server checks that the installation belongs to the token’s application and resolves the human installer itself; clients never submit an arbitrary user ID to act as. application metadata, installation enumeration, gateway ticket and command-registration routes are application-level exceptions. bot credentials are rejected by existing human /api routes. browser CORS is disabled for bot-token endpoints.
// Use request() from the quickstart.
await request('/chats/chat_example/messages', {
installation: 'install_example', key: 'message-job-42',
actor: 'bot', content: 'hello!',
});token lifecycle
tokens contain at least 256 bits of random secret material, are stored as hashes and are redacted from logs. allow at most two active application tokens for explicit rotation overlap; the developer creates a replacement and revokes the old token. tokens have no automatic expiry in v1, but are revocable immediately. revocation closes sessions authenticated with that token. the token can enumerate only its application’s minimal installation metadata; possession never overrides user grants.
common object conventions
IDs are opaque case-sensitive strings up to 128 characters; never parse their format. timestamps are RFC 3339 UTC strings. revisions and sequence counters are decimal strings, preventing integer precision loss. lists return {data, next_cursor}; cursor is an opaque signed token tied to application, installation, filters and authorization revision. limit defaults to 50 and ranges from 1 to 100. null next_cursor means complete. list order is documented per resource and stable by (created_at,id); message history uses sequence ascending with before/after cursors.
{
"id": "install_example",
"application_id": "app_example",
"user_id": "user_example",
"configuration_revision": "3",
"grant_revision": "1",
"status": "active",
"scope": {
"type": "chat",
"chat_id": "chat_example"
},
"permissions": [
"commands",
"bot.messages.ephemeral"
],
"authorized_since": {},
"created_at": "2026-10-20T07:00:00Z",
"optional_permissions": []
}validation, concurrency and retries
JSON requests use UTF-8 and reject unknown fields rather than silently dropping them. PATCH changes only supplied fields; null clears only explicitly nullable fields. mutable-resource PATCH/DELETE and state replacements require If-Match with the last returned ETag; stale writes return 412. set-membership operations (pins, reactions, blocks), upload cancellation, outgoing-request cancellation and session cleanup are idempotent without an existing ETag except where a route explicitly requires one; whole settings/vote/group replacements require one. create/action POSTs require an Idempotency-Key of 1–128 ASCII characters, except gateway ticket issuance, call heartbeats and transient scribble previews. keys are scoped to application, installation, method and route for 24 hours; same key/body returns the stored receipt, different body returns 409. recheck authorization before returning cached receipts. resource and outbox event commits are atomic. GET is side-effect free and never marks read.
errors and redaction
errors include a stable code, safe lowercase message, request_id and optional field details, missing_permissions or retry_after_ms. missing_permissions lists only known application grants. inaccessible resources return 404 to avoid existence leaks. reject expired/disabled credentials with 401; denied grants, paused installs and group exclusion use 403 when the installation is already known. do not retry permanent validation or authorization failures.
scroll the table horizontally to see all columns.
| status | codes | handling |
|---|---|---|
| 400 | invalid_request, invalid_cursor, unsupported_actor, invalid_configuration | fix fields or start a fresh pagination query |
| 401 | invalid_token, revoked_token | replace credentials; never retry indefinitely |
| 403 | permission_denied, installation_paused, application_disabled, group_bot_excluded | inspect grants/status; request user action when appropriate |
| 404 | not_found | resource absent or not visible |
| 409 | configuration_changed, idempotency_conflict, already_exists, invalid_state | reload state or use the correct idempotency key |
| 410 | interaction_expired, cursor_expired, upload_expired | restart the relevant flow; do not replay stale actions |
| 412 | revision_mismatch | reload writable fields and explicitly merge |
| 413 / 415 | payload_too_large / unsupported_media | reduce size or use a supported media type |
| 429 | rate_limited, quota_exceeded | honor Retry-After and inspect quota metadata |
| 503 | temporarily_unavailable | bounded exponential retry with jitter and the same key |
{
"error": {
"code": "permission_denied",
"message": "this installation cannot send messages.",
"request_id": "req_example",
"missing_permissions": [
"bot.messages.send"
]
}
}identity and discovery
installation enumeration returns only ID, installer basic identity, approved revision, scope, grants and status. installation-scoped discovery filters every chat and permission by current access. chats.list is an intrinsic capability: it returns ID, kind and title needed to address grants, with no last-message preview, unread count or member roster.
scroll the table horizontally to see all columns.
| method and path | request | result |
|---|---|---|
| GET /applications/@me | application token | public Application + enabled/status + published revision |
| GET /installations | cursor, limit; application token | application’s Installation list |
| GET /installations/@current | installation header | Installation + effective_permissions |
| GET /limits | installation header | limits, field schemas and granted quota usage |
| GET /chats | cursor, limit; installation header | eligible Chat{id,kind,title} list |
| GET /chats/{chat} | installation header | Chat{id,kind,title}; no previews or receipts |