Overview
Reliable integrations handle failures in two directions: your system calling the Mobaro API, and Mobaro sending webhooks to your endpoint. This article covers the HTTP responses the Mobaro API returns, the rate limits behind them and how to react to each, then the webhook-receiver side at the end.
The Mobaro API returns a consistent set of status codes across endpoints. Your integration should detect them and react: fix the request or key, or retry safely.
At a glance |
|
Who can do this | Anyone calling the Mobaro API with an API key. Writes need a key without Read-only. Creating keys needs Organization › Administrate. |
Where | API keys: Configuration › API › API Keys |
Works on | Public API |
Availability | Enabled per organization by Mobaro — ask your CSM |
Mobaro API response codes
Code | What it means | What to do |
400 Bad Request | The request failed validation, for example a | Fix the request. Do not retry unchanged. |
401 Unauthorized | Missing, invalid or deleted API key in the | Check the header and the key. Do not retry until fixed. |
403 Forbidden | A Read-only key was used for a write ( | Use a key without Read-only for writes. Do not retry. |
404 Not Found | The record or route doesn't exist. | Check IDs and routes. Do not blind-retry. |
429 Too Many Requests | You hit a rate limit. See Mobaro API rate limits. | Wait, then retry with exponential backoff and jitter. |
500 Internal Server Error | Something went wrong on Mobaro's side while handling the request. | Retry a few times with backoff. For writes, check whether the change was applied first. If it persists, contact Mobaro Support. |
503 Service Unavailable | The Mobaro API is temporarily switched off on Mobaro's side. | Retry later with backoff and jitter; give up after a few attempts and alert your team. |
Mobaro API rate limits
Rate limits are counted per API key, with one exception for User writes:
What | Limit | Counted |
All API requests | 10 requests per second | Per API key |
Location group writes (create, update, move, relations, delete) | 1 request per 15 seconds | Per API key |
When you go over a limit, one extra request is held briefly and then processed; any further requests get 429 straight away.
⚠️ Heads-up: The 429 response doesn't include a Retry-After header, so use your own backoff. Because the User write limit is shared, you can get a 429 on User writes even when your own volume is low; back off and retry.
Handle each API error
400 Bad Request
Read the response body: it lists each invalid field with a message, for example
Allowed values are: ...for an unsupportedOrderBy.Common causes:
Limitabove the maximum (128 on most list endpoints, 20 on Results), a malformed date, or a missing required field. See Mobaro API parameter reference.
401 Unauthorized
Send the key in the
X-Api-Keyheader on every request.Confirm the key still exists under Configuration › API › API Keys. A deleted key no longer works. See Creating and Managing API Keys.
Only retry after fixing the key. Blind retries waste time and count toward your rate limit.
403 Forbidden
The key is marked Read-only, and read-only keys can't create, update or delete data.
Use a separate key without Read-only for integrations that write, and keep read-only keys for reporting.
404 Not Found
Re-check the resource path and the ID (for example
/users/{id},/timesheets/{id}).If you're looking up by filters, list first, then get by ID.
429 Too Many Requests
Back off and retry with exponential backoff (for example 1 s, 2 s, 4 s, 8 s) and jitter.
Page through large datasets with
LimitandOffsetinstead of firing many requests in parallel.Give separate integrations separate keys, so one busy job doesn't slow another. This doesn't apply to User writes, which share one limit.
503 Service Unavailable
Treat it as temporary. Retry with exponential backoff and jitter.
Stop after a capped number of attempts (for example 3–6) and alert your team.
Safe API retry pattern
This pseudocode stops on errors you must fix and backs off on 429 and 503:
maxAttempts = 5
delay = 1s
for attempt in 1..maxAttempts:
resp = call_api()
if resp.status in [200..299]: return resp
if resp.status in [400, 401, 403, 404]:
abort (fix the request, key or IDs; do not auto-retry)
if resp.status in [429, 503]:
sleep(delay + random_jitter())
delay *= 2
continue
raise error with context (for 500, see below)
raise error "max retries exceeded"
Retry helper code samples
Python (requests)
import time, random, requests
API = "https://app.mobaro.com/api/customers/users"
HEADERS = {"X-Api-Key": "YOUR_SECRET_TOKEN"}
def get_with_retry(url, headers, attempts=5, base=1.0):
delay = base
for i in range(attempts):
r = requests.get(url, headers=headers)
if 200 <= r.status_code < 300:
return r
if r.status_code in (400, 401, 403, 404):
raise RuntimeError(f"Non-retryable {r.status_code}: {r.text}")
if r.status_code in (429, 503):
time.sleep(delay + random.uniform(0, 0.5))
delay *= 2
continue
r.raise_for_status()
raise TimeoutError("Max retries exceeded")
resp = get_with_retry(API, HEADERS)
print(resp.json())
Node.js (built-in fetch)
async function getWithRetry(url, opts = {}, attempts = 5, base = 1000) {
let delay = base;
for (let i = 0; i < attempts; i++) {
const res = await fetch(url, opts);
if (res.ok) return res;
if ([400, 401, 403, 404].includes(res.status)) {
throw new Error(`Non-retryable ${res.status}: ${await res.text()}`);
}
if (res.status === 429 || res.status === 503) {
await new Promise(r => setTimeout(r, delay + Math.random() * 250));
delay *= 2;
continue;
}
throw new Error(`${res.status} ${await res.text()}`);
}
throw new Error("Max retries exceeded");
}
const res = await getWithRetry(
"https://app.mobaro.com/api/customers/users",
{ headers: { "X-Api-Key": "YOUR_SECRET_TOKEN" } }
);
console.log(await res.json());Receive Mobaro webhooks reliably
Everything above is about your system calling the Mobaro API. When Mobaro sends webhooks to your endpoint, the roles reverse and you're the server:
Return a 2xx within 5 seconds. Acknowledge the delivery quickly and do heavier processing afterwards. A slower response, or any non-2xx status, counts as a failed delivery.
Be idempotent. The same notification can arrive more than once. The payload is
{ event, resource, timestamp, data }, and there is no separate object ID field: useresource,event,data.idandtimestamptogether as your key.Expect out-of-order arrival. A failed notification waits for its retry while newer ones go through, so compare
data.updatedwith what you already have.Let Mobaro retry. Failed deliveries are retried automatically with increasing delays, up to once every 24 hours, with no attempt limit. You don't need your own catch-up polling for short outages.
ℹ️ Note: For webhook setup and the payload, see Using webhooks in Mobaro. For the notification list and retrying a notification straight away, see Monitoring webhook deliveries and retrying failed events.
Best practices
Pace bulk jobs below the limits rather than relying on retries: for example, no more than about 8 requests per second per key, one location group write every 15 seconds, and User writes one at a time. For how keys and limits work, see Understanding API access scopes and limitations.
Log the endpoint, status code, attempt number and delay used.
Alert on repeated 401s or 403s (key or configuration problem) and spikes of 429 or 503 (load or scheduling issue).
Record the last successful page or ID so your job can resume after a failure.
Make creates and updates idempotent (for example, look up by external ID before creating) so retries are safe.
Frequently asked questions
Why do I get 401 Unauthorized when my API key is correct?
Mobaro didn't accept the X-Api-Key header: it's missing, incomplete, or the key was deleted. A common cause is pasting the Prefix from the API Keys list; the full key is shown only once, when it's created. If you don't have it, create a new key.
GET works, but POST, PUT or DELETE fails. Why?
A 403 means the key is Read-only; use a key without it for writes. Otherwise, check the operation is listed in the Mobaro API documentation: not every backend action is in the API. For example, you can't add a comment to an Assignment through the API.
Does a 401 or 403 mean the key's user or Role lacks permissions?
No. API keys aren't linked to a user, Role or User Group, so there are no permissions to adjust. A 401 means the key itself wasn't accepted; a 403 means a Read-only key tried to write. See Understanding API access scopes and limitations.
