Errors
Every error answers with Content-Type: application/problem+json and the body {type, title, status, detail, instance, code, errors?}. The type is https://app.mondochat.io/problems/<code>.
errors is set for validation: a list of {loc, msg}. reason names the rule for policy_denied and limit_reached, and for some forbidden and banned answers, for example guests_disabled or temporary. A bad number or a NUL character in your input answers 422 validation, never 500.
An answer for a missing or expired token, as the API sends it:
{ "type": "https://app.mondochat.io/problems/unauthorized", "title": "Unauthorized", "status": 401, "detail": null, "instance": "/v1/me", "code": "unauthorized"}detail is null unless the route says more, for example token already used. reason and errors are left out when they do not apply.
| code | status | meaning |
|---|---|---|
unauthorized |
401 | The token is missing, invalid or expired. |
forbidden |
403 | You are authenticated but not allowed. reason can say why. |
banned |
403 | The user or the IP is banned. detail carries the support email if set. reason is temporary when the ban expires. |
policy_denied |
403 | The app settings forbid it. reason names the rule. |
limit_reached |
403 | A daily limit was reached. reason names it. |
not_found |
404 | Missing, or in another tenant. It is never 403 for another tenant. |
conflict |
409 | The state conflicts with the request. |
busy |
409 | The callee is in a call or was rung in the last 45 seconds. |
idempotency_conflict |
409 | The same Idempotency-Key was sent with a different body. |
gone |
410 | No longer available: a view-once message opened more than 60 seconds ago, or a sync cursor the server no longer serves (reason: stale_cursor). On stale_cursor, reload, then sync from the new latest_seq. |
precondition_failed |
412 | The If-Match version is stale. |
validation |
422 | The input is invalid. errors lists each field. |
rate_limited |
429 | You sent too many requests. Wait Retry-After seconds. |
upstream_failed |
502 | Your callout endpoint or a provider failed. |
unavailable |
503 | The database is resuming or busy. Retry-After: 5. |
On POST /v1/device/session, a 401 unauthorized carries login_url: the pwa.login_url setting as set, with {lang} left to fill, or null.
A blocked recipient is not an error. The send succeeds for the sender and is not delivered.
Rate limits
Section titled “Rate limits”Rate limited routes set the headers RateLimit-Policy and RateLimit. A request over its limit answers 429 rate_limited with Retry-After.
- Per app: 50 user requests per second by default, set by
rate_limits.app_requests_per_second. Each API server counts on its own, so the total across servers can be higher. - Per role, in settings
limits:messages_per_minute(30 by default) answers 429rate_limited.new_conversations_per_day,photos_per_day(photos and videos together),voice_per_day(100 by default) andmessages_to_unansweredanswer 403limit_reachedwith the limit’s name asreason. A limit set to null is unlimited. - A tenant in
teststatus accepts at most 50 users per app. The 51st answers 403forbiddenwith reasontest_user_limit. Ask us to move the tenant tolive. - Calls ring for 45 seconds and end after 240 minutes by default (
calls.ring_timeout_seconds,calls.max_duration_minutes). - A client token may live at most 600 seconds after
iat. A session token lasts 900 seconds. An app has at most 2 active server secrets and 20 origins, and one endpoint that answers callouts.
Retry safely
Section titled “Retry safely”Unsafe server to server calls take an Idempotency-Key header. A repeat with the same key and body within 24 hours replays the stored response. The same key with another body answers 409 idempotency_conflict.