Skip to content

Getting started

Overview

Min (MinChat) developer documentation: REST API, realtime events and the Telegram-compatible Bot API.

Min is a messenger with web, desktop and Android clients. Everything the official clients do goes through the public HTTP API described here: a JSON REST API under /api, a Pusher-compatible WebSocket for realtime events, and a separate Telegram-compatible Bot API.

ItemValue
REST base URLhttps://minchatapp.com/api
Bot APIhttps://minchatapp.com/bot<token>/<method>
RealtimeReverb (Pusher protocol) — settings from GET /api/broadcasting/client-config
FormatJSON (application/json); uploads — multipart/form-data
AuthAuthorization: Bearer <token> (Laravel Sanctum)
OpenAPI/docs/openapi.json

Quick start#

Sign in, list your chats and send a message:

curl
# 1. Sign in (identifier-first wizard)
TICKET=$(curl -s -X POST https://minchatapp.com/api/auth/start \
  -H 'Content-Type: application/json' -H 'Accept: application/json' \
  -d '{"identifier":"anna@example.com"}' | jq -r .ticket)

TOKEN=$(curl -s -X POST https://minchatapp.com/api/auth/password \
  -H 'Content-Type: application/json' -H 'Accept: application/json' \
  -d "{\"ticket\":\"$TICKET\",\"password\":\"Secret123\"}" | jq -r .token)

# 2. List conversations
curl -s https://minchatapp.com/api/conversations \
  -H "Authorization: Bearer $TOKEN" -H 'Accept: application/json'

# 3. Send a message
curl -s -X POST https://minchatapp.com/api/conversations/12/messages \
  -H "Authorization: Bearer $TOKEN" -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  -d '{"content":"Hello from the API 👋"}'

Authentication#

All protected endpoints require a Sanctum personal access token in Authorization: Bearer <token>. Tokens are issued by the sign-in endpoints (password, one-time code, QR login, Google OAuth) and never expire on their own; every token is a session that the user can see and terminate in Settings → Devices (GET /api/user/sessions). Banned accounts receive 403 with code: "banned".

Optional client headers that label the session:

HeaderMeaning
X-Client-Platformweb, desktop, android, …
X-Client-VersionApp version, e.g. 1.0.45
X-Device-NameHuman-readable device name
Accept-LanguageSets the account locale on registration (ru / en)

See Authentication for every sign-in flow.

Errors#

Errors use one envelope. code is a stable machine-readable identifier (domain.snake_case); error and message contain the same English text. Validation errors (422) add errors (field → messages) and error_codes (field → codes). The full catalog is on the Errors page.

json
{
  "success": false,
  "code": "validation.failed",
  "error": "The given data was invalid.",
  "message": "The given data was invalid.",
  "errors": { "send_at": ["The time is too far in the future."] },
  "error_codes": { "send_at": ["validation.failed"] }
}
StatusTypical codes
400Operation not applicable (e.g. conversation.private_not_editable)
401common.unauthenticated, auth.invalid_credentials, auth.oauth_expired
403common.forbidden, banned, conversation.not_member, conversation.banned
404common.not_found, conversation.channel_not_found, bot.not_found
409Conflicts (e.g. channel owner leaving: conversation.owner_must_transfer)
419common.csrf — cookie session without Bearer and expired CSRF
422validation.failed, message.empty, …
429common.throttled, auth.too_many_attempts
500 / 503common.server_error; 503 when a dependency (e.g. calls/LiveKit) is not configured

Rate limits#

Limits are per route and per user (or per IP for public routes). Each endpoint in this reference shows its limit, e.g. 60/min. When exceeded the API answers 429 with code: "common.throttled" and the standard headers Retry-After, X-RateLimit-Limit, X-RateLimit-Remaining. Routes without an explicit limit are still protected server-side; do not poll faster than once per second.

AreaLimit
Sending messages60/min
Editing/deleting, reactions, polls60/min
Search (messages, users)30/min
Typing status, drafts, mentions read120/min
Sign-in10–20/min; /register 5 per 10 min
Bot API (per route)120/min
Call heartbeat240/min

Pagination#

There is no single pagination scheme; each list uses the cursor that fits its data:

EndpointScheme
GET /conversations/{id}before_id / after_id / around_id + limit (≤ 100), has_more
GET /conversations/{id}/messages/searchbefore_id, 20 per page
GET /messages/searchbefore_id → next_before_id, 30 per page
GET /conversations/{id}/mediaoffset/limit in attachments → next_offset, has_more
GET /conversations/{id}/infoafter + after_role → members_has_more
GET /conversations/{id}/bansafter (ban_id) + limit → bans_has_more
GET /conversationsnot paginated (all chats)
Bot getUpdatesoffset (confirm) + limit ≤ 100

Conventions#

  • IDs are integers; attachment ids are UUID strings.
  • Timestamps are ISO 8601 in UTC (e.g. 2026-10-22T09:06:13+00:00 or …000000Z). Bot API uses Unix time (date).
  • Booleans in multipart forms may be sent as 1/0.
  • Send Accept: application/json so errors come back as JSON.
  • Send client_id (UUID) with messages to make retries idempotent.
  • Attachment URLs (/storage/media/…) require the same Bearer token — send it only to the Min host.

Versioning#

The API is not versioned in the URL. It evolves additively: new fields and endpoints may appear at any time, so ignore unknown fields. Breaking changes are avoided; legacy endpoints (e.g. /api/login, /api/files/…) stay available for older clients. Native clients are steered through GET /api/client-updates (update: soft|force).

Public web URLs#

URLPage
/c/{username}Public channel page
/c/{username}/{message}Public post
/channelsChannel catalog
/join/{code}Group/channel invite
/u/{token}, /u/{username}Contact link
/e/{slug}Emoji set
/invite/{code}Registration invite
/webWeb app