Set up your webhook endpoint
Webhooks are configured in the portal under Integrations > Webhooks:1
Prepare a receiving endpoint
A publicly reachable HTTPS URL that accepts
POST requests with a JSON body and responds 200 quickly. Perform.AI must be able to reach it directly — allowlist by hostname rather than IP if you filter inbound traffic.2
Register the endpoint
In Integrations > Webhooks, add your endpoint URL.
3
Copy the Authentication Key
The page provides the Authentication Key used to sign every webhook with HMAC-SHA256 — store it in your secret manager; you’ll verify signatures with it (below). HMAC is the default scheme; if you need a different one (OAuth 2.0, JWT, Basic, static key), raise it with your account team.
4
Activate triggers
Choose which notification triggers fire webhooks (next section). No active triggers means no webhooks.
The payload format version (5.0 / 5.2 / 5.3) is set per account, not per endpoint — confirm or change yours with your account team before integrating, and parse accordingly.
Triggers
Triggers define when a webhook fires — on every event, or only on specific milestones. They’re managed with your notification configuration in the portal. Each delivered payload’strigger array names every activated trigger that the update cycle satisfied. Values derive from the trigger’s name (lowercased, punctuation replaced by underscores). Standard triggers include:
Accounts using Returns also have return-lifecycle triggers (for example
returns_all_events), and custom triggers can be set up with your account team — their payload values follow the same naming derivation (for example a “Successfully delivered + all events” trigger emits successfully_delivered_all_events).
One webhook per update cycle: when several activated triggers fire from the same batch of new events, you receive a single webhook whose trigger array lists them all — deduplicate by shipment and updated_date, not by trigger.
How delivery works
- HTTPS POST with a JSON body.
- Respond with HTTP 200. Anything else — including a timeout — counts as a failure: Perform.AI retries up to 3 times, 5 minutes apart, then stops. Missed webhooks are not replayed afterwards; reconcile gaps with the List Shipments API filtered by
updated_date.
Verify the sender
Every request carries anHTTP-X-Hmac-SHA256 header: the HMAC-SHA256 of the raw request body, keyed with your Authentication Key. Recompute and compare:
The payload
data carries the full Shipment — identifiers, carrier, status and phase, addresses, costs, documents — plus three event views:
The field-by-field payload is documented per format version in the reference:
Format 5.0
The baseline payload.
Format 5.2
Adds returns, line items, collection point, rating, enriched event locations.
Format 5.3
Restructured EDD by source + Performance Aspiration results.
Format versions
Formats are additive on the same envelope — build your parser to ignore unknown fields and upgrades are non-breaking, with one exception: 5.3 restructures
expected_delivery (below).
Field behaviors worth knowing
- Event timestamps vary in precision. Each event’s
timereflects what the source provided — full timestamp with offset, timestamp without timezone, or date only — withtimezoneset only when known. See timestamps. expected_delivery(formats 5.0/5.2) is a simplefrom/towindow and is omitted entirely when neither merchant nor carrier provided an EDD — don’t assume the key exists.expected_delivery(format 5.3) becomes four source objects —merchant,carrier,predict_engine, anddisplay— and unlike earlier formats the object is always present, with a source set tonullwhen it has no estimate (the value shown in the product, following your account’s configurable EDD display priority). Each carriesstart,end, atype, and timezones. Precision differs by source:merchantandcarrierestimates are always date-only ranges (type: date_range— no time component, and merchant timezones are alwaysEtc/UTC), whilepredict_engineprovides full datetime ranges (datetime_range). Checktypebefore parsing rather than assuming a time exists.pa_results(format 5.3) is an empty array unless the Performance Aspiration module is enabled on your account. When populated,expected_result_datecan benullfor hour-based transit-time rules.- Units are normalized:
dimensionsalways in centimeters,weightalways in grams, money fields as{amount, currency}objects. notification_typeis currently alwaysshipment_notification— treat it as a discriminator for future notification types, not information.- Address objects mirror the Shipment’s stored addresses; their
email/phoneare contact data, not notification subscriptions.