Errors and retries
Every error from the JustAI data APIs tells you whether to fix the request or to retry it. A 5xx error, a 429, or a timeout is temporary. Retry those with backoff, and keep the same body and messageId so that JustAI applies the request only once. Any other 4xx error means that the request, the key, or the URL needs a change. An unchanged request fails the same way again.
Status codes
Section titled “Status codes”| Status | API | Body | Cause | Action |
|---|---|---|---|---|
400 |
All | {"error":"Invalid org slug"} |
The path value is not a valid slug. For example, it has an uppercase letter. | Fix the slug in the path. |
400 |
All | {"error":"Invalid environment"} |
The X-JustAI-Environment header is empty or badly formatted, for example Prod. |
Send the exact environment key, or leave the header out for Production. |
400 |
Identify | {"error":"Invalid Identify call","issues":[...]} |
The body failed validation. | Fix each listed issue. See Identify errors. |
400 |
Identify | {"error":"timestamp cannot be more than 5 minutes in the future"} |
timestamp is more than 5 minutes ahead of JustAI’s clock. |
Check your clock and the time zone of the timestamp. |
400 |
Track | {"error":"<message>"} |
The body failed validation. The message describes the first problem. | Fix the body. See Track errors. |
401 |
All | {"error":"Missing API key"} |
The request has no key in X-Api-Key or Authorization. |
Add the key. |
401 |
All | {"error":"Invalid API key"} |
The key is unknown or revoked. | Check the key’s status in Settings > API keys, or create a new key. |
403 |
All | {"error":"Forbidden"} |
The key, organization, path value, and environment do not match, or the environment was removed. | Use a key created in the same environment as the path value and header. |
404 |
All | 404 Not Found (plain text) |
The path is unknown, the method is wrong, or the path ends with a slash. | Fix the URL and the method. |
404 |
Profiles | {"error":"Profile not found"} |
No profile in this environment has this user ID. | Check the user ID and the environment. After the first Identify call for a user, wait a minute and read again. |
409 |
Identify | {"error":"messageId <id> was already used for a different call"} |
The messageId was used before with a different userId, timestamp, or traits. |
Send the update with a new messageId. |
413 |
Track | {"error":"Track event is too large"} |
The event is over the size limit of about 1 MB. | Make the event smaller. |
429 |
All | Not always JSON | A network layer between your system and JustAI asked you to slow down. | Retry with backoff, and lower your concurrency. |
500 |
All | {"error":"Internal error"} |
An unexpected server error. | Retry with backoff. |
503 |
Track | {"error":"Event storage is unavailable"} |
JustAI could not accept the event at that moment. | Retry with backoff, with the same body and messageId. |
Order of checks
Section titled “Order of checks”JustAI checks each request in this order and returns the first failure:
- The path and method match an endpoint (
404). - The slug in the path has a valid format (
400). - The
X-JustAI-Environmentheader, if present, has a valid format (400). - A key is present (
401). - The key is valid (
401). - The key, organization, path value, and environment match (
403). - The request body is valid (
400,409, or413).
A bad slug or a bad environment header returns 400 even when the request has no key.
Error body shapes
Section titled “Error body shapes”| Shape | Returned by | Example |
|---|---|---|
| Error string | Every JSON error | {"error":"Forbidden"} |
| Error string with issues | Identify validation errors | {"error":"Invalid Identify call","issues":[{"code":"custom","message":"At least one trait is required unless consent is provided","path":["traits"]}]} |
| Plain text | An unknown path or method | 404 Not Found |
The two write APIs report validation errors differently:
- Identify lists every issue. Each issue has a
code, apathto the field, and amessage. - Track returns only the first problem’s message, and the message does not name the field. For example, a missing
timestampreturns{"error":"Required"}.
Retry temporary failures
Section titled “Retry temporary failures”Retry these responses:
500and503429- a timeout, or a connection that closes before you get a response
Do not retry 400, 401, 403, 404, 409, or 413 until you change the request, the key, or the URL. They fail the same way every time, with two exceptions:
- A Profiles
404right after the first Identify call for a user. Wait a minute and read again. - A
401with{"error":"Invalid API key"}just after you create a key. Wait briefly and try again; confirm it works before switching traffic.
Use bounded exponential backoff with jitter. The values below are a starting point. Tune them for your system.
- Wait a short time, such as 1 second, before the first retry.
- Double the wait after each failed attempt, up to a maximum wait, such as 1 minute.
- Add a random amount to each wait, so that your clients do not all retry at the same moment.
- Stop after a fixed number of attempts. Keep the failed request so that you can replay it later.
On every retry, resend the identical request: the same body and the same messageId. For Identify, this includes the same timestamp, because a changed timestamp under the same messageId returns 409.
If many requests fail with 5xx at the same time, lower your concurrency before you retry.
When you do not know whether a request succeeded
Section titled “When you do not know whether a request succeeded”A timeout does not mean that the request failed. JustAI can accept a request before the connection drops. Retry with the same messageId:
- Identify returns
200with"duplicate": trueif it already applied the call. - Track returns
202again and counts the event once.
Duplicates and idempotency
Section titled “Duplicates and idempotency”messageId makes retries safe on both write APIs. Each API handles a repeated messageId in its own way:
| Behavior | Identify | Track |
|---|---|---|
messageId |
Optional. JustAI generates one if you leave it out. | Required. |
| Identical retry | 200 with "duplicate": true. JustAI applies nothing again. |
202. JustAI counts the event once and starts a journey at most once. |
Same messageId, different content |
409. |
202. Do not do this. Send a correction as a new event with a new messageId. |
Follow these rules for both APIs:
- Use a new
messageIdfor every new update or event. - Reuse a
messageIdonly to retry the same request. - Send every consent change with a new
messageId. Identify does not compareconsentwhen it checks for a duplicate, so a reused ID drops the new decision. - Never reuse a Track
messageIdto correct an event. JustAI can’t edit or delete an accepted event. If you send a corrected event with a newmessageId, JustAI records it in addition to the original, and it can start a matching journey again. To remove events, contact your JustAI team.
When to leave out the Identify messageId
Section titled “When to leave out the Identify messageId”Send your own messageId on Identify when you can give every new update a new ID. Leave messageId out only when your sync tool can’t guarantee a new ID whenever a synced value changes. JustAI then generates a new ID for each call, so it can’t recognize a retry and treats every retry as a new update. When you leave messageId out, always send timestamp, so a delayed retry can’t overwrite a newer value.
For warehouse and reverse ETL syncs, build the messageId from values that change whenever any synced value changes, such as a hash of the user ID, the update time, and every synced trait. The Hightouch profile sync template builds it this way. An ID built only from user_id and updated_at returns 409 when a trait changes but updated_at doesn’t.
JustAI remembers each Identify messageId per environment, so the same ID in Production and in another environment doesn’t collide.
Log errors without exposing secrets
Section titled “Log errors without exposing secrets”Log enough to find and replay a failed request:
- the endpoint path and method
- the status code and the response body
- the
messageIdand the user ID - the attempt number and the time
Never log an API key. Remove the X-Api-Key and Authorization headers before you log a request. When traits or properties hold personal data, log the record’s IDs instead of the full payload. When you contact your JustAI team about a request, send its messageId, never the key.
