Errors & Limits
Errors are JSON with an error field (and often a machine-readable code). Messages are sanitized —
URLs, S3 keys, and filesystem paths are replaced with [internal].
Status codes
Section titled “Status codes”// 400 Bad Request{ "error": "Missing required field: email" }
// 401 Unauthorized{ "error": "Invalid or missing token" }
// 403 Forbidden{ "error": "You do not have permission to perform this action", "required": "owner", "current": "editor" }
// 404 Not Found{ "error": "Room not found" }
// 500 Internal Server Error{ "error": "Internal server error" }Transient failures worth retrying (502 / 503)
Section titled “Transient failures worth retrying (502 / 503)”Writes that change a room’s live document can fail for reasons that are nobody’s fault and clear on their own:
// 502 — the change could not be applied to the live room document{ "error": "Failed to update marker" }
// 503 — the room's state was momentarily unreadable; nothing was changed{ "error": "Room state temporarily unavailable" }503 is safe to retry as-is: the request was refused before touching anything, rather than
applied blindly on top of a state the server could not confirm. Back off briefly and repeat.
Token not allowed (fail-closed)
Section titled “Token not allowed (fail-closed)”A room API token used on an endpoint outside its allowlist:
{ "error": "api_token_not_allowed", "message": "API tokens cannot access this endpoint. Tokens are limited to room-scoped data operations." }A cross-room or missing-permission token on an allowed endpoint returns a plain 403 with a reason
like "API token is not scoped to this room" or "API token lacks '<perm>' permission".
Upload size limit (413)
Section titled “Upload size limit (413)”Any single multipart upload over 50 MB is rejected (never a 500):
{ "error": "file_too_large", "maxBytes": 52428800, "message": "File exceeds the 50 MB single-request limit. Use the chunked endpoints for larger files." }Use the chunked upload endpoints (or the import script) for larger files.
Storage quota (413 / 403)
Section titled “Storage quota (413 / 403)”Storage is charged to the room owner and checked before any bytes are persisted:
{ "error": "quota_exceeded", "kind": "storage_total", "limit": 10737418240, "current": 10500000000, "attempted": 300000000, "message": "Storage limit exceeded for this account" }kind: "storage_total" is a 413; admin-imposed room-count caps return 403 with
kind: "room_count". Free up space (delete data) or move to a larger plan.
Rate limiting & CORS
Section titled “Rate limiting & CORS”Public, rate-limited surfaces (such as map tiles) apply per-IP limits and return 429 with a
Retry-After header when exceeded; clients should back off. Marker and comment writes are
rate-limited per caller as well — batch work should pace itself rather than fire in a tight loop. Browser clients are served from the
app origin (https://collmap.com), which CORS permits with the Authorization and Content-Type headers.

