Skip to content

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 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.

JustAI checks each request in this order and returns the first failure:

  1. The path and method match an endpoint (404).
  2. The slug in the path has a valid format (400).
  3. The X-JustAI-Environment header, if present, has a valid format (400).
  4. A key is present (401).
  5. The key is valid (401).
  6. The key, organization, path value, and environment match (403).
  7. The request body is valid (400, 409, or 413).

A bad slug or a bad environment header returns 400 even when the request has no key.

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, a path to the field, and a message.
  • Track returns only the first problem’s message, and the message does not name the field. For example, a missing timestamp returns {"error":"Required"}.

Retry these responses:

  • 500 and 503
  • 429
  • 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 404 right after the first Identify call for a user. Wait a minute and read again.
  • A 401 with {"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.

  1. Wait a short time, such as 1 second, before the first retry.
  2. Double the wait after each failed attempt, up to a maximum wait, such as 1 minute.
  3. Add a random amount to each wait, so that your clients do not all retry at the same moment.
  4. 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 200 with "duplicate": true if it already applied the call.
  • Track returns 202 again and counts the event once.

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 messageId for every new update or event.
  • Reuse a messageId only to retry the same request.
  • Send every consent change with a new messageId. Identify does not compare consent when it checks for a duplicate, so a reused ID drops the new decision.
  • Never reuse a Track messageId to correct an event. JustAI can’t edit or delete an accepted event. If you send a corrected event with a new messageId, JustAI records it in addition to the original, and it can start a matching journey again. To remove events, contact your JustAI team.

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 enough to find and replay a failed request:

  • the endpoint path and method
  • the status code and the response body
  • the messageId and 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.