POST requests, one request per event. This guide is for the developer who builds that endpoint. An integration admin sets up the subscription in Lobyco Nexus and gives you its signing key.
Before you start
- A callback URL. Give the admin an HTTPS address at a public host name. Nexus refuses an IP address, and Lobyco refuses an address in a private network when the subscription is saved and again on every request.
- An authentication method. Agree with the admin how Lobyco signs in to your endpoint: OAuth 2.0 client credentials, HTTP Basic or custom headers. The admin stores it in Nexus as the subscription’s auth integration. Authentication describes the three types.
- The signing key. Nexus shows it once, when the subscription is saved; get it from the admin. It is a base64 string. Keep it in a secret store, never in source code.
- A durable store. A table or a queue where your endpoint keeps each event before it answers. Lobyco recommends one keyed on the delivery id, as in Use the idempotency key.
Handle a delivery
Your endpoint does the same for every request:1
Read the request without parsing it
Read the headers, and read the body as raw bytes. Do not parse the JSON yet: the signature covers the exact bytes Lobyco sent, and parsing and re-serialising changes them. The request lists every header.2
Check the credentials
Check theAuthorization header, or your custom headers, against the credentials in the subscription’s auth integration. Answer 401 to a request without valid credentials. This keeps anonymous callers out before your endpoint does any other work.3
Check the signature
Compute the HMAC-SHA256 of<t>.<webhook id>.<raw body> with the signing key, and compare it with every v1 value in x-lobyco-signature. Answer 401 when none matches. Verify the signature has the details, code and a test vector.4
Claim the delivery id
Insert thex-lobyco-webhook-id value into your store in one atomic write. If the id is already there, this is a repeat: answer 2xx and stop. Use the idempotency key has the rules.5
Store the event and answer 2xx
Store the raw body with its id, then answer any2xx status. Lobyco ignores the response body. Answer within 10 seconds, counted from the start of the attempt, including Lobyco’s sign-in to your endpoint. A later 2xx counts as a timeout, and Lobyco sends the event again.6
Process the event from your store
Process the event after you have answered, from your store. Route it onx-lobyco-event, for example offer.redeemed. Skip webhook.test, which is a test request from Nexus. Do not rely on the order events arrive in: see Handle events out of order.7
Check it works with a test
Ask the admin to select Test on the subscription in Nexus. Nexus shows your status code and response time. A Success with your2xx means the credentials, the address and your signature check all work.Then ask for Test payload on one of the subscription’s events. Your endpoint gets a sample body in the shape it will receive for that event, again marked webhook.test.The request
The four
x-lobyco-* header names are lower case and reserved. A custom-headers integration cannot override them. It can set its own User-Agent or Accept, which then replaces Lobyco’s.
The body
By default, the body is the event’s data exactly as the owning service published it. Lobyco does not wrap it in an envelope, rename fields or add fields: everything Lobyco adds is in the headers. The body therefore matches the event’s schema in the Events catalogues. If the admin set a payload transformation for the event, the body is the JSON it produces instead. The headers are the same either way, and the signature covers the body actually sent. The publishing services follow different conventions, and your parser must accept all of them:Verify the signature
Lobyco signs every delivery with HMAC-SHA256 and the subscription’s signing key. Signing cannot be switched off. Check the signature before you trust the body.- What is signed: the text
<t>.<webhook id>.followed by the raw request body.<t>is thetvalue fromx-lobyco-signature, and<webhook id>is thex-lobyco-webhook-idheader. - The key: base64-decode the signing key and use the resulting 32 bytes as the HMAC key. Do not use the text of the base64 string.
- The header:
t=<unix seconds>, v1=<hex>, with a comma and a space between entries. While the admin rotates the key, the header carries onev1entry per valid key. Accept the request if any entry matches.
- Read the raw body before any JSON parsing.
- Split
x-lobyco-signatureon commas, trim each part, and readtand everyv1value. - Reject the request if
tis more than 5 minutes from your clock. Lobyco recommends this replay window but does not enforce one. A retry carries a fresht. - Compute
HMAC-SHA256(key, "<t>." + webhook id + "." + raw body)as lowercase hex. - Compare it with each
v1value in constant time. Accept the request if one matches.
t, x-lobyco-webhook-id and the body. It does not cover x-lobyco-event or x-lobyco-timestamp. Apply your replay window to t, never to x-lobyco-timestamp, and treat x-lobyco-event as routing, not as proof.
Test your implementation
Both rows use the raw body{"offerId":"offer-1"}, t = 1758182400 and the webhook id 9c36b36d1f50d772916352c6499fd695ebdece031df828e05aa454c02b34e7fe.
During a rotation from the second key to the first, the header is
t=1758182400, v1=4fcc76…e4aba4, v1=fd9567…a9f597. A check with either key must accept it.
This t is in the past, so switch off the replay window for this test only, never in production.
Rotate the key
The admin can generate a new signing key in Nexus at any time, and Nexus shows it once. The previous key stays valid for 24 hours. During that time, every delivery carries av1 signature for each of the two keys.
- Add the new key next to the old one, and accept a request that matches either.
- Remove the old key within 24 hours.
Use the idempotency key
x-lobyco-webhook-id is the idempotency key. Lobyco sets it once per subscription and published event. It repeats it unchanged on every retry and on every resend by Lobyco Support.
- Verify the signature first. The signature covers the id, so the id on a request that passes the check is genuine. Never record the id of a request that fails it.
- Claim the id in one atomic write. Insert it into a table keyed on the id, or use a conditional put. If the write succeeds, this is the first copy. If it conflicts, it is a repeat. Do not read first and write later: a second copy can arrive while you still handle the first, for example after a timeout.
- Answer
2xxto a repeat. Any other answer counts as a failure: Lobyco retries the delivery or sets it aside, as in Failure and edge cases. - Do not let the check lose an event. Store the event together with its id and process it afterwards. Or record the id in the same transaction as the work it triggers. If you record the id and the work then fails, the retry is skipped as a repeat and the event is lost.
- Keep the ids for at least 30 days. Retries run for about 45 hours, and Lobyco Support can resend a set-aside delivery for up to 30 days, with the same id. Expiring ids after 31 days covers both.
EventId alone: on member.updated it changes when the event is published again.
Handle events out of order
Do not rely on the order events arrive in.- Events about the same customer normally arrive in the order they happened. A retried delivery can still be overtaken by a later event, and a resend by Lobyco Support can arrive days later.
- Definition events have no order at all. An
inbound_api.updatedcan arrive before theinbound_api.createdof the same Inbound API block. - A member’s loyalty-card events are ordered per card. They can arrive before or after that member’s other events.
*.created, *.updated), every inbound_api.* event and member.updated carry the full current state of the item. Keeping the newest version you have received is therefore safe. Decide which version is newer from the time of the change, never from the order of arrival:
- For most events, compare the event’s timestamp.
- For an Inbound API event, compare the time of its own change per
triggerId:createdAt,updatedAt,publishedAt,stoppedAtordeletedAt.
Recognise test requests
The admin can test a subscription from Nexus in two ways. Both send one request, signed and authenticated like a real delivery, withx-lobyco-event: webhook.test.
-
Test sends a fixed body:
-
Test payload sends a sample of one of the subscription’s events. It has real field names and made-up values, run through the event’s saved payload transformation. Only the
x-lobyco-eventheader tells it apart from a real event.
webhook.test request as real data. Verify its signature and answer 2xx, like any other request. Test requests are never retried.
Secure the endpoint
- Protect personal data. Check the data carried by customer events and Inbound API events. A delivery Lobyco could not deliver is kept, body included, for 30 days.
- Revoke credentials through the admin. To stop Lobyco using a set of credentials, ask the admin to switch the subscriptions to another auth integration, or to disable them. Deleting the integration is not enough: see Manage a subscription.
Limits
- No delivery history for you. You cannot see your failed deliveries or request a replay. Lobyco Support handles deliveries that were set aside.
- No automatic pause. A subscription stays enabled even if every delivery fails, until an admin disables it.
- No rate limit per subscription. Lobyco limits how many deliveries run at the same time and honours
Retry-After. Answer429withRetry-Afterto slow Lobyco down. - Managed in Nexus only. There is no API to manage subscriptions.
Failure and edge cases
Your answer decides what Lobyco does next:
Lobyco’s sign-in to your endpoint follows the same rule. If your identity provider is unavailable, with a timeout, a
429 or a 5xx, Lobyco retries. If it refuses the credentials or answers 404, the delivery is set aside without retrying.
A delivery has up to 10 attempts. Before each retry, Lobyco waits a little longer, give or take 20%:
That is a window of about 45 hours. If every attempt fails, the delivery is set aside.
- Set-aside deliveries are kept for 30 days, then deleted. Lobyco Support can resend them once the cause is fixed, with the same
x-lobyco-webhook-id. - Disabling discards waiting deliveries. During an outage, ask the admin to leave the subscription enabled: see Manage a subscription.
- A failed payload transformation sends nothing. When the transformation cannot produce valid JSON for an event, Lobyco sets the delivery aside without calling your endpoint. Ask the admin to fix it, then ask Lobyco Support to resend.
Related
- Data streams: set up a subscription — what the admin does in Nexus: events, auth integration, signing key, tests and enabling.
- Events — event types, Data streams availability, timing and JSON Schemas.
- OutboundAPI connection — the auth integration types, and how a campaign calls your system at one chosen step.