taking longer than usual.

skip to content
sometimes/ developers

developers / bots / protocol

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.

API 1 · REST + gatewaydownload reference

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.

JSON
{
  "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.

statuscodeshandling
400invalid_request, invalid_cursor, unsupported_actor, invalid_configurationfix fields or start a fresh pagination query
401invalid_token, revoked_tokenreplace credentials; never retry indefinitely
403permission_denied, installation_paused, application_disabled, group_bot_excludedinspect grants/status; request user action when appropriate
404not_foundresource absent or not visible
409configuration_changed, idempotency_conflict, already_exists, invalid_statereload state or use the correct idempotency key
410interaction_expired, cursor_expired, upload_expiredrestart the relevant flow; do not replay stale actions
412revision_mismatchreload writable fields and explicitly merge
413 / 415payload_too_large / unsupported_mediareduce size or use a supported media type
429rate_limited, quota_exceededhonor Retry-After and inspect quota metadata
503temporarily_unavailablebounded exponential retry with jitter and the same key
JSON
{
  "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 pathrequestresult
GET /applications/@meapplication tokenpublic Application + enabled/status + published revision
GET /installationscursor, limit; application tokenapplication’s Installation list
GET /installations/@currentinstallation headerInstallation + effective_permissions
GET /limitsinstallation headerlimits, field schemas and granted quota usage
GET /chatscursor, limit; installation headereligible Chat{id,kind,title} list
GET /chats/{chat}installation headerChat{id,kind,title}; no previews or receipts