Skip to Content
FR8 Factory Docs

Delivery

Retries, idempotency, timeouts, and how to catch up after an outage.

Retries

On 5xx, 429, a timeout, or a connection failure: exponential backoff across roughly 24 hours.

A suggested schedule of delays between attempts:

1m → 5m → 30m → 2h → 6h → 15h        six attempts, ≈24h total

A quick first retry covers a momentary blip; the long tail covers a real outage. The shape matters more than the exact numbers — if your platform has a standard retry policy, use it and tell us the window.

Do not retry 400 or 401. Those will fail identically on every attempt; see Errors.

Sign every attempt at the moment you send it

A retry carries the same body and the same eventId, but a new t and therefore a new signature. Do not store the original request and replay it as-is: its t is outside the five-minute window by the time the second attempt goes out, so it is rejected with 400 — and under the rule above, never tried again. The signature is a property of the attempt, not of the event.

Idempotency

Every retry must carry the eventId of the original attempt, not a fresh one.

We store events keyed on that id with a unique constraint, so a duplicate delivery returns 202 and does nothing else. Retrying is free for you and safe for us. A new id per attempt turns a retry into a second event, which is the one way to make at-least-once delivery produce duplicate work.

Timeouts

Please allow a 10-second read timeout.

We expect to answer in well under a second — the work is verify, insert, respond — but a cold start under load should not be read as a failure and trigger a retry that was never needed.

Catching up after a gap

Retries stop after about a day, and a 401 is never retried. Both leave a permanent hole if something was wrong on either side for long enough.

So we would like a way to ask for events we missed. Either shape works:

A)  GET /getEventsSince?cursor=<eventId|timestamp>     paged
B)  an operator action that re-sends a given time window

(A) is considerably better — it lets us recover automatically, without involving anyone at your end. (B) still beats nothing, because it turns a silent gap into a support request rather than a permanent one.

Why this is worth building

Without a catch-up path, every gap has to be closed by falling back to the polling this integration exists to replace — which means we can never fully retire it, and the integration keeps a manual recovery step forever.

With one, the webhook becomes genuinely self-healing: we notice a gap, ask for the range, and catch up. It is the difference between a delivery mechanism and a reliable one.

If neither is feasible, tell us. We will keep a reconciliation sweep running against /getLoadsByList permanently instead, and size its cadence to how much loss we are willing to carry.

Ordering

We do not ask for ordering guarantees, and we would rather you did not hold events back to deliver them in sequence. Promptness is worth more to us, and in this version there is one event per load anyway.

occurredAt is still required on every event. We use it to guard against a stale delivery overwriting fresher data — which costs you nothing now, and is exactly what we would need on the day update events arrive.

Batching

None, please. One event per request.

A batch endpoint means partial failures, and "which of these fifty did you accept" becomes a protocol we would both have to invent and then maintain.

On this page

Help

How do I get API access?

API keys are issued per customer by the FR8 Factory team. Ask your account contact — or send us a request through the Help desk tab with your company name and use case.

How do I authenticate requests?

Send your API key in the Authorization header on every request. Keys are secret — never ship them in browser code or mobile apps.

Open the guide — How do I authenticate requests?

How do I keep my copy of the data in sync?

Use delta polling: request only records changed since your last sync cursor instead of re-downloading everything. The guide covers cursors, ordering, and replay safety.

Open the guide — How do I keep my copy of the data in sync?

What are the rate limits?

Limits are per API key and documented on the rate-limits page, along with the response headers that tell you where you stand and how to back off.

Open the guide — What are the rate limits?

What does each load field mean?

The load field dictionary defines every field the API returns — types, nullability, and freight-domain meaning (pick vs drop dates, AR, equipment types).

Open the guide — What does each load field mean?

Found a bug or missing data?

Send it through the Help desk tab with the request you made and what you expected back. You'll get a reference code, and we'll email you when it's resolved.