REST API
Authentication
Sign-in flows: identifier-first wizard, one-time codes, QR login, Google OAuth, sessions and devices.
Min has no cloud password or 2FA. The recommended flow is the identifier-first wizard: start → (password | request-code → verify-code) → token. New accounts finish with register. Each step is bound by a 48-character ticket.
POST /api/auth/start {identifier}
├─ next=password ─▶ POST /api/auth/password {ticket,password} ─▶ {token,user}
│ └▶ POST /api/auth/request-code {ticket} ─▶ POST /api/auth/verify-code {ticket,code} ─▶ {token,user}
├─ next=code ─────▶ POST /api/auth/verify-code ─▶ {next:"name"} ─▶ POST /api/auth/register {ticket,name,password}
└─ next=invite ───▶ POST /api/auth/register {ticket,name,password,invite_code}
QR login#
- The signed-out device calls
POST /api/auth/qrand showsqr(minchat-login:<token>) as a QR code. - It subscribes to the public channel from
channel(qr-login.<hash>) and/or pollsPOST /api/auth/qr/pollevery 1–2 s. - A signed-in phone scans it, calls
inspectto show the request, thenconfirmordecline. pollreturnsstatus: confirmedwithtokenanduserexactly once.
Google OAuth (web)#
Open /auth/google in the browser. After the callback the user lands on /web with a one-time oauth_exchange cookie; the web app immediately calls POST /auth/oauth-exchange to receive the Bearer token.
Sign-in, registration, password#
Start sign-in (identifier-first)
Step 1 of the sign-in wizard. Normalises the identifier (email → lowercase, phone → E.164), looks the account up and decides the next step. Returns a ticket (48 chars) that links the following steps. next is password for existing accounts, code for a new account outside Russia (a one-time code is sent), invite for a new account in Russia (registration by invite only). There is no cloud password / 2FA.
Parameters
| Name | Type | Description |
|---|---|---|
identifier
required body
|
string |
Email or phone number. max 255 chars
|
Request
curl -X POST 'https://minchatapp.com/api/auth/start' \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-d '{"identifier": "anna@example.com"}'
Response
{
"success": true,
"next": "password",
"ticket": "NOtN0Li5lPE01BIr6Gx6iBAOspXxUp9f9nBmnxWPOlsLcosG",
"region": "other",
"can_request_code": true
}
Sign in with password
Step 2 for existing accounts. Returns a Sanctum token and the user.
Parameters
| Name | Type | Description |
|---|---|---|
ticket
required body
|
string |
Ticket from /auth/start.exactly 48 chars
|
password
required body
|
string |
Account password. |
Request
curl -X POST 'https://minchatapp.com/api/auth/password' \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-d '{"ticket": "<ticket>", "password": "Secret123"}'
Response
{
"success": true,
"user": {
"id": 3,
"name": "Vera Kotova",
"email": "vera@example.com",
"created_at": "2026-10-20T11:12:46.000000Z",
"updated_at": "2026-10-22T09:12:49.000000Z",
"avatar": null,
"last_seen_at": "2026-10-22T09:12:49.000000Z",
"push_notifications_enabled": true,
"invited_by_id": null,
"invite_id": null,
"locale": "ru",
"default_history_ttl": 0,
"phone": null,
"about": null,
"birthday": null,
"username": null,
"is_bot": false,
"phone_verified_at": null,
"avatar_url": null
},
"token": "17|Q2pXexampleSanctumToken0000000000000000000"
}
401 wrong
{
"success": false,
"code": "auth.ticket_invalid",
"error": "Sign-in session expired. Start again.",
"message": "Sign-in session expired. Start again."
}
Request a one-time code
Sends a one-time login code instead of the password. Delivery: an active MinChat client of the account (in-app, realtime event LoginCodeRequested), SMS for non-RU phones, or email. In Russia only in-app delivery is available. Cooldown: SMS 60 s, email/in-app 30 s.
Parameters
| Name | Type | Description |
|---|---|---|
ticket
required body
|
string |
Ticket from /auth/start.exactly 48 chars
|
Request
curl -X POST 'https://minchatapp.com/api/auth/request-code' \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-d '{"ticket": "<ticket>"}'
Response
Responds with HTTP 200.
Resend the one-time code
Same as request-code, respects the cooldown.
Parameters
| Name | Type | Description |
|---|---|---|
ticket
required body
|
string |
Ticket from /auth/start.exactly 48 chars
|
Request
curl -X POST 'https://minchatapp.com/api/auth/resend-code' \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-d '{"ticket": "<ticket>"}'
Response
Responds with HTTP 200.
Verify the one-time code
Existing account → returns token + user. New account (outside Russia) → {"next":"name"}, continue with /auth/register.
Parameters
| Name | Type | Description |
|---|---|---|
ticket
required body
|
string |
Ticket from /auth/start.exactly 48 chars
|
code
required body
|
string |
6-digit code. max 12 chars
|
Request
curl -X POST 'https://minchatapp.com/api/auth/verify-code' \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-d '{"ticket": "<ticket>", "code": "123456"}'
Response
Responds with HTTP 200.
Finish registration
invite_code is required only for the ru region. Returns token + user.
Parameters
| Name | Type | Description |
|---|---|---|
ticket
required body
|
string |
Ticket from /auth/start.exactly 48 chars
|
name
required body
|
string |
Display name. max 255 chars
|
password
required body
|
string |
New password. requires
password_confirmationmin 8, letters + digits |
invite_code
optional body
|
string |
Invite code (RU only). max 32 chars
|
Request
curl -X POST 'https://minchatapp.com/api/auth/register' \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-d '{"ticket": "<ticket>", "name": "Anna Smith", "password": "Secret123", "password_confirmation": "Secret123"}'
Response
Responds with HTTP 200.
Sign in with email + password (legacy)
One-step login kept for older clients; new clients should use /auth/*. After 5 failed attempts in 15 minutes from one IP the endpoint answers 429.
Parameters
| Name | Type | Description |
|---|---|---|
email
required body
|
string |
Account email. email
|
password
required body
|
string |
Password. |
Request
curl -X POST 'https://minchatapp.com/api/login' \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-d '{"email": "anna@example.com", "password": "Secret123"}'
Response
{
"success": true,
"user": {
"id": 3,
"name": "Vera Kotova",
"email": "vera@example.com",
"created_at": "2026-10-20T11:12:46.000000Z",
"updated_at": "2026-10-22T09:12:48.000000Z",
"avatar": null,
"last_seen_at": "2026-10-22T09:12:48.000000Z",
"push_notifications_enabled": true,
"invited_by_id": null,
"invite_id": null,
"locale": "ru",
"default_history_ttl": 0,
"phone": null,
"about": null,
"birthday": null,
"username": null,
"is_bot": false,
"phone_verified_at": null,
"avatar_url": null
},
"token": "17|Q2pXexampleSanctumToken0000000000000000000"
}
401 401
{
"success": false,
"code": "auth.invalid_credentials",
"error": "Invalid email or password",
"message": "Invalid email or password"
}
Register with email (legacy)
Legacy registration. Same response as /login.
Parameters
| Name | Type | Description |
|---|---|---|
name
required body
|
string |
Display name. max 255 chars
|
email
required body
|
string |
Email. emailunique
|
password
required body
|
string |
Password. requires
password_confirmationmin 8, letters + digits |
invite_code
optional body
|
string |
Invite code (when invites are required). |
Request
curl -X POST 'https://minchatapp.com/api/register' \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-d '{"name": "Anna Smith", "email": "anna@example.com", "password": "Secret123", "password_confirmation": "Secret123", "invite_code": "ABCDEF"}'
Response
Responds with HTTP 200.
Sign out
Revokes the current token, or all tokens of the account with ?all=1 (or {"all":true}). Revoked sessions receive SessionsRevoked.
Parameters
| Name | Type | Description |
|---|---|---|
all
optional body
|
boolean |
Revoke every token of the account. |
Request
curl -X POST 'https://minchatapp.com/api/logout' \
-H "Authorization: Bearer $TOKEN" \
-H 'Accept: application/json'
Response
{
"success": true,
"code": "auth.logged_out",
"message": "Logged out."
}
Send a password reset link
Always answers with a neutral message. client: desktop → minchat://reset-password/…, android/app → minchatapp://reset-password/…, otherwise the web URL /password/reset/{token}.
Parameters
| Name | Type | Description |
|---|---|---|
email
required body
|
string |
Email address. emailmax 255 chars
|
client
optional body
|
string |
Where the link should open: desktop, android, app or web (default). |
Request
curl -X POST 'https://minchatapp.com/api/forgot-password' \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-d '{"email": "anna@example.com", "client": "android"}'
Response
{
"success": true,
"code": "password.reset_sent",
"message": "If an account exists, we sent a password reset email."
}
Reset the password
Uses the token from the email.
Parameters
| Name | Type | Description |
|---|---|---|
token
required body
|
string |
Token from the reset email. |
email
required body
|
string |
Email address. email
|
password
required body
|
string |
Password. requires
password_confirmationmin 8, letters + digits |
Request
curl -X POST 'https://minchatapp.com/api/reset-password' \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-d '{"token": "<from-email>", "email": "anna@example.com", "password": "NewSecret123", "password_confirmation": "NewSecret123"}'
Response
Responds with HTTP 200.
Change the password
Requires the current password.
Parameters
| Name | Type | Description |
|---|---|---|
current_password
required body
|
string |
Current password. |
password
required body
|
string |
Password. requires
password_confirmationmust differ from current_passwordmin 8, letters + digits |
Request
curl -X POST 'https://minchatapp.com/api/change-password' \
-H "Authorization: Bearer $TOKEN" \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-d '{"current_password": "Secret123", "password": "NewSecret123", "password_confirmation": "NewSecret123"}'
Response
{
"success": false,
"code": "password.wrong_current",
"error": "Current password is incorrect.",
"message": "Current password is incorrect."
}
Create a QR login request
Called by a signed-out desktop/web client. Render qr as a QR code; it lives expires_in seconds (90). Then either poll /auth/qr/poll with poll_token or subscribe to the public channel channel and wait for QrLoginUpdated.
Parameters
No parameters.
Request
curl -X POST 'https://minchatapp.com/api/auth/qr' \
-H 'Accept: application/json'
Response
{
"qr": "minchat-login:sBopO38GonBMQ39qB3d2lEGwXPEdkKV6QYR96PqYFQ1Bq4M8",
"poll_token": "3KFapxV62nDlATwjomaI5HqHB9HyBCVTYo7TqNX6pxdNsVjp",
"channel": "qr-login.e4a27d213ac0f5cc88a2d3df9fb46f3c53315953b9f5082e89e088fd78844e5a",
"expires_at": "2026-10-22T09:07:43+00:00",
"expires_in": 90
}
Poll a QR login request
status: pending, confirmed (the response then contains token and user — the token is handed out once), consumed, declined, expired. An unknown token also returns expired.
Parameters
| Name | Type | Description |
|---|---|---|
poll_token
required body
|
string |
poll_token from /auth/qr.max 128 chars
|
Request
curl -X POST 'https://minchatapp.com/api/auth/qr/poll' \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-d '{"poll_token": "<poll_token>"}'
Response
{
"status": "expired"
}
Inspect a scanned QR code
The signed-in phone shows who is asking to sign in (device, platform, IP, location) before confirming.
Parameters
| Name | Type | Description |
|---|---|---|
token
required body
|
string |
The scanned QR payload ( minchat-login:…) or its token part. |
Request
curl -X POST 'https://minchatapp.com/api/auth/qr/inspect' \
-H "Authorization: Bearer $TOKEN" \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-d '{"token": "minchat-login:<token>"}'
Response
Responds with HTTP 200.
Confirm QR login
Issues a new session for the requesting device. The requester receives QrLoginUpdated {status:"confirmed"}.
Parameters
| Name | Type | Description |
|---|---|---|
token
required body
|
string |
The scanned QR payload ( minchat-login:…) or its token part. |
Request
curl -X POST 'https://minchatapp.com/api/auth/qr/confirm' \
-H "Authorization: Bearer $TOKEN" \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-d '{"token": "minchat-login:<token>"}'
Response
Responds with HTTP 200.
Decline QR login
The requester receives QrLoginUpdated {status:"declined"}.
Parameters
| Name | Type | Description |
|---|---|---|
token
required body
|
string |
The scanned QR payload ( minchat-login:…) or its token part. |
Request
curl -X POST 'https://minchatapp.com/api/auth/qr/decline' \
-H "Authorization: Bearer $TOKEN" \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-d '{"token": "minchat-login:<token>"}'
Response
Responds with HTTP 200.
Google sign-in (web redirect)
Browser redirect to Google. Optional ?invite=CODE is kept in the session for registration. Not under /api.
Parameters
| Name | Type | Description |
|---|---|---|
invite
optional query
|
string |
Invite code for new accounts. |
Request
curl 'https://minchatapp.com/auth/google' \
-H 'Accept: application/json'
Response
Responds with HTTP 200.
Google OAuth callback
On success redirects to /web and sets a short-lived httpOnly cookie oauth_exchange (2 minutes). Errors redirect to /?error=… (invite_required, account_banned, google_auth_failed, …).
Parameters
No parameters.
Request
curl 'https://minchatapp.com/auth/google/callback' \
-H 'Accept: application/json'
Response
Responds with HTTP 200.
Exchange the Google OAuth code for a token
The web app calls this right after the redirect. Reads the oauth_exchange cookie (one-time) and returns {success:true, token}; 204 when there is nothing to exchange, 401 auth.oauth_expired when it has expired.
Parameters
No parameters.
Request
curl -X POST 'https://minchatapp.com/auth/oauth-exchange' \
-H 'Accept: application/json'
Response
Responds with HTTP 200.
Sessions & devices#
List sessions (devices)
Every Sanctum token of the account is a session. current marks the token of this request. platform/client/app_version/device_name come from the X-Client-Platform, X-Client-Version, X-Device-Name headers and the User-Agent; location is derived from the IP.
Parameters
No parameters.
Request
curl 'https://minchatapp.com/api/user/sessions' \
-H "Authorization: Bearer $TOKEN" \
-H 'Accept: application/json'
Response
{
"sessions": [
{
"id": 1,
"current": true,
"label": "Laptop",
"device_name": "Chrome, macOS",
"client": "web",
"app_version": null,
"ip": "203.0.113.10",
"location": "Moscow, Russia",
"platform": "Web",
"created_at": "2026-10-20T11:12:45+00:00",
"last_used_at": "2026-10-22T09:06:13+00:00"
},
{
"id": 19,
"current": false,
"label": "Chrome",
"device_name": "Mac",
"client": "desktop",
"app_version": "1.0.44",
"ip": "203.0.113.10",
"location": null,
"platform": "macOS",
"created_at": "2026-10-21T07:17:33+00:00",
"last_used_at": "2026-10-21T07:18:34+00:00"
}
]
}
Terminate a session
Revokes one token; that device receives SessionsRevoked.
Parameters
| Name | Type | Description |
|---|---|---|
id
required path
|
integer |
Session (token) id. |
Request
curl -X DELETE 'https://minchatapp.com/api/user/sessions/19' \
-H "Authorization: Bearer $TOKEN" \
-H 'Accept: application/json'
Response
{
"success": true
}
Terminate all other sessions
Revokes every token except the current one.
Parameters
No parameters.
Request
curl -X DELETE 'https://minchatapp.com/api/user/sessions' \
-H "Authorization: Bearer $TOKEN" \
-H 'Accept: application/json'
Response
{
"success": true,
"terminated": 11
}