When you send your own invites, host the tracking experience in your own website or app, or collect ratings with your own tool, Bettermile no longer sees how consignees engage with their parcels. Sharing that engagement data closes the loop: it lets Bettermile measure the tracking experience end to end, compare channels, and improve ETAs and features for your consignees.
You share it by sending one request per parcel and day to the consignee engagement webhook.
Share engagement data if any of the following is true for your integration:
- You send the tracking invites yourself. See Invites via Tenant.
- The tracking page lives in your website or app. See Co Owned Experience and Deep Integration.
- You collect ratings with a third-party or custom tool. See 3rd Party Tool and Custom Rating.
Each request describes one parcel on one day. Only tenantId, parcelNumber and date are required. All other sections are optional: send only the ones you collect. For example, if you only send your own invites, send only invites; if you only run your own feedback tool, send only ratings.
| Section | What it describes |
|---|---|
invites | The tracking invite you sent: channel, whether it was delivered, opened and clicked. |
clicks | Every visit to the tracking page or app, with the ETA window the consignee saw and how they arrived (utmSource). |
ratings | The rating and feedback the consignee gave, including the scale and the tool used. |
outOfHome | An out-of-home delivery option the consignee chose, such as a parcel locker or a safe place. |
otherFeatures | Other tracking features the consignee used, such as the map view. |
The Consignee Engagement API reference lists every field, its format and the allowed values, with an example request for each section on its own.
The example below shows a tenant that sends its own invites, hosts the tracking page and collects ratings:
{
"tenantId": "gls-de",
"parcelNumber": "P123456789",
"date": "2026-09-16",
"invites": {
"createdAt": "2026-09-16T09:06:00Z",
"inviteMedium": "EMAIL",
"destinationUrl": "https://www.example.com/track/P123456789",
"delivered": true,
"opened": true,
"clicked": true
},
"clicks": [
{
"createdAt": "2026-09-16T09:20:00Z",
"platform": "WEB",
"url": "https://www.example.com/track/P123456789?utm_source=invite-email",
"etaLowerBound": "2026-09-16T10:15:00Z",
"etaUpperBound": "2026-09-16T10:35:00Z",
"utmSource": "invite-email"
}
],
"ratings": {
"createdAt": "2026-09-16T12:03:00Z",
"platform": "WEB",
"scale": "ONE_TO_FIVE",
"tool": "STARS",
"ratingValue": 5,
"feedbackMessage": "Perfect service!",
"errors": []
}
}Each request is a snapshot: the complete state of one parcel on one day, not what changed since your last request. Whenever something new happens, send everything you know about the parcel and day again, including all earlier clicks. A later request for the same parcelNumber and date replaces the earlier one, so if you collect several sections, send them together in each request rather than one section per request.
This also makes retries safe: sending the same snapshot twice has no side effects.
You decide when to send. Sending after every new event gives Bettermile the most current picture; sending once at the end of the delivery day is also fine, as long as the snapshot is complete.
Requests are authenticated with an API key in the x-api-key header. Bettermile issues one key per tenant; request it from your Bettermile representative. The tenant is taken from the key, so keep it secret and use it only from your backend.
curl -X POST https://data-solutions.bettermile.com/parcel-consignee-engagement \
-H "x-api-key: $BETTERMILE_ENGAGEMENT_API_KEY" \
-H "Content-Type: application/json" \
-d @engagement.jsonThe request body is validated strictly. Every object is closed, so a field that is not part of the schema is rejected with 400, as is a wrongly formatted date or a value outside the allowed range. If you want to send data that does not fit the schema yet, contact Bettermile first so the field can be added.
| HTTP status | Meaning | What to do |
|---|---|---|
202 | Accepted for storage. | Nothing. Do not retry. |
400 | The body failed validation. | Fix the request. Do not retry it unchanged. |
403 | The API key is missing, unknown or not allowed to send engagement data. | Check the x-api-key header. Do not retry. |
429 | Throttled. | Retry with exponential backoff and jitter. |
500, 503 | Temporary failure. | Retry with exponential backoff and jitter. |
Successful and throttled responses include a requestId. Log it and quote it when contacting Bettermile about a request.
- Send only what you collect. Leave out sections you have no data for instead of sending them empty.
- Always send the full snapshot. A request with only the newest click replaces the earlier clicks for that parcel and day.
- Use UTC timestamps in ISO 8601 format, for example
2026-09-16T09:06:00Z. - Use the documented values for
scale,tool,utmSource,outOfHome.typeandfeatureType, so your data can be compared with that of other channels and tenants. - Use the same UTM sources as in your invites, for example
utm_source=invite-emailandutm_source=invite-sms. See Invites via Tenant. - Retry only on
429and5xx, with exponential backoff and jitter. Retrying on400or403will not succeed. - Test in the development environment first. Ask your Bettermile representative for a development API key.