Webhooks
Get a signed HTTPS call when trips, quotes, travelers or documents change.
bymundi calls your HTTPS endpoint when something changes — whoever changed it: someone in the app, the assistant, or the API.
Subscribe
Create an endpoint with your key (or in Integrations → API & MCP → Webhooks):
curl -X POST https://api.bymundi.com/v1/webhook-endpoints \
-H "Authorization: Bearer $BYMUNDI_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"url":"https://example.com/hooks/bymundi","eventTypes":["trip.updated","quote.accepted"]}'
The answer includes the endpoint's signing secret (whsec_…), shown once. An endpoint belongs to the key that created it: it reads with that key's permissions and stops when the key is revoked. A key can have up to 10.
The event
{
"id": "evt_…",
"type": "trip.updated",
"timestamp": "2027-05-01T09:00:00Z",
"subject": { "object": "trip", "id": "…", "tripId": "…" },
"changed": ["title", "startDate"],
"data": { "object": "trip", "id": "…", "title": "…" }
}
data is the object exactly as its REST GET returns it, read when the event is delivered. changed names the fields that changed. Bursts of edits are grouped: an *.updated event is sent 5 seconds after the last edit, and at most 60 seconds after the first.
Verifying signatures
Deliveries follow Standard Webhooks. Three headers:
webhook-id— the event id; the same on every retry.webhook-timestamp— Unix seconds.webhook-signature—v1,<base64>; during a secret rotation, two space-separated signatures.
The signature is HMAC-SHA256 over {webhook-id}.{webhook-timestamp}.{raw body}, keyed with the base64-decoded part of your secret after whsec_. Verify against the raw body, before parsing it, and refuse timestamps more than 5 minutes away:
import crypto from "node:crypto";
export function verify(secret, headers, rawBody) {
const id = headers["webhook-id"], ts = headers["webhook-timestamp"];
if (Math.abs(Date.now() / 1000 - Number(ts)) > 300) return false;
const key = Buffer.from(secret.replace(/^whsec_/, ""), "base64");
const expected = crypto.createHmac("sha256", key).update(`${id}.${ts}.${rawBody}`).digest("base64");
return headers["webhook-signature"].split(" ").some((s) => {
const sig = s.split(",")[1] ?? "";
return sig.length === expected.length && crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected));
});
}
Any Standard Webhooks library (JavaScript, Python, Go, Ruby, PHP…) does the same.
Answering
Answer any 2xx within 10 seconds; do slow work afterwards. Anything else — or no answer — is retried after 5 seconds, 5 minutes, 30 minutes, 2 hours, 5 hours, 10 hours and 10 hours. An endpoint with no successful delivery for 3 days is switched off; turn it back on with PATCH and enabled: true.
- Delivery is at least once. Deduplicate by
webhook-id. - Order is not guaranteed. Compare
timestamp, or re-read the object. - Missed events stay available for 30 days at
GET /v1/events.
Events
trip.created— A trip was created.trip.updated— A trip's fields or metadata changed.changednames the fields.trip.archived— A trip was archived.datais null: an archived trip is not readable (see GET /trips/archived).trip.restored— An archived trip was restored.trip.published— A trip was published to its travelers.trip.unpublished— A trip was unpublished.trip.deleted— A trip was erased for good.datais null.trip.itinerary.updated— A trip's itinerary changed — one event per burst of edits.datais the TRIP; read the tree with GET /trips/{tripId}/itinerary.quote.created— A quote was created.quote.updated— A quote changed.changednames the fields.quote.sent— A quote was sent (or sent again).quote.accepted— A quote was accepted.quote.rejected— A quote was rejected.quote.deleted— A quote was deleted.datais null.traveler.added— A passenger was added to a trip. Personal data.traveler.updated— A passenger changed.changednames the fields. Personal data.traveler.removed— A passenger was removed.datais null.trip.contact.updated— A trip's booking contact was made, changed or removed (datanull when there is none). Personal data.document.created— A document finished uploading.document.updated— A document was renamed, moved or released.changednames the fields.document.deleted— A document was deleted.datais null.
Operations
- List webhook endpoints —
GET /v1/webhook-endpoints - Create a webhook endpoint —
POST /v1/webhook-endpoints - Get a webhook endpoint —
GET /v1/webhook-endpoints/{endpointId} - Update a webhook endpoint —
PATCH /v1/webhook-endpoints/{endpointId} - Delete a webhook endpoint —
DELETE /v1/webhook-endpoints/{endpointId} - Rotate a webhook endpoint's secret —
POST /v1/webhook-endpoints/{endpointId}/rotate-secret - Send a test event —
POST /v1/webhook-endpoints/{endpointId}/test - List an endpoint's deliveries —
GET /v1/webhook-endpoints/{endpointId}/deliveries - Resend a delivery —
POST /v1/webhook-endpoints/{endpointId}/deliveries/{deliveryId}/resend - List events —
GET /v1/events - Get an event —
GET /v1/events/{eventId}