Skip to content

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 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 429 rate_limited. new_conversations_per_day, photos_per_day (photos and videos together), voice_per_day (100 by default) and messages_to_unanswered answer 403 limit_reached with the limit’s name as reason. A limit set to null is unlimited.
  • A tenant in test status accepts at most 50 users per app. The 51st answers 403 forbidden with reason test_user_limit. Ask us to move the tenant to live.
  • 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.

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.