Inferences
Events
When an inference is created. an event is emitted which can be listened to via a webhook to retrieved via the events endpoint.
Shipment Events
inference.transformed is emitted for shipping-label inferences only. The other inference types (bill of lading, item label, document classification, VLM) are never transformed into a tracker, so they never emit it.
It is also emitted on failure as well as success: if tracker creation fails, the event still arrives and the inference carries status: "error" with the reason in errors. Read status rather than treating the event itself as confirmation that a tracker exists.
Known gap: no event when the organization has no default rate
There is one failure that emits no event at all. If your organization has no settings.deliveries.default_rate_id, tracker creation stops before it starts: the inference is set to status: "error" with missing_rate_id in errors, and neither inference.transformed nor inference.updated is emitted.
A webhook consumer waiting on inference.transformed will therefore wait indefinitely for these inferences. Until this is changed, either make sure a default rate is configured, or reconcile against GET /v1/inferences/images/shipping-labels?errors=missing_rate_id rather than relying on the event alone. Callers using options.tracker.create_synchronously are unaffected, because the failure is in the response body they already read.
Ordering
An inference that is transformed into a tracker emits two events, always in this order:
inference.created, carryingstatus: "transforming"inference.transformed
This holds whether the tracker is created asynchronously (the default) or inside the request with options.tracker.create_synchronously. The count and the order are the same either way, so you can build one lifecycle and not branch on how the organization is configured.
What differs is only timing: with synchronous creation both events are emitted during the request, so inference.transformed can arrive while you are still reading the HTTP response. With the default, inference.transformed follows a short time later.
When it is a later update that finally moves an inference to transforming - you supplied the contact that was missing, say - the pair is inference.updated followed by inference.transformed, in that order, and with synchronous creation stored on the inference both are again emitted during that update's request.
Note that inference.created carries status: "transforming" in both cases, which is the state at the moment the event was emitted. If you fetch the inference on receiving that event, you may already see completed (or error) instead. That is expected, and is why the response body or a fresh read is the authority on the current state rather than the event payload.
An inference that never reaches transforming emits only inference.created. With several images that includes the missing_label and ambiguous_label outcomes: the inference is created with status: "error", no tracker is ever attempted, and so no inference.transformed follows. A consumer keyed on inference.transformed should read status and errors off the inference.created payload to close these out - the payload is the complete inference, images and the retained extraction included, so nothing has to be fetched afterwards. See Multiple Images Per Inference.
One multi-image failure emits nothing at all: when extraction failed on every image and the failure was on our side (a 5xx), the upstream error becomes the request's error, so the row is recorded with status: "error" but no inference.created is emitted. You learn about that one from the failed HTTP call rather than from a webhook. Reconciling for it means filtering on status rather than on a specific error code, because the row carries none - only status: "error" and each image's own error. There is no code to filter on and no separate marker for this path, so status is the only thing to branch on. See How the winning image is chosen.
An image that could not be fetched, decoded or read emits nothing either, and unlike the case above leaves no row behind - the request fails before the inference is created, so there is nothing to reconcile against and the failed HTTP response is the only record.