Abandoned Checkout
End-to-end recipe for modelling abandoned checkouts as a custom event — schema, mapping, and payload.
A worked example: model "shopper started checkout but didn't complete it" as a custom event called checkout_abandoned, register it, send a signed payload that LiveRecover validates and records, and trigger a recovery flow off it.
This recipe covers registering the event and getting a payload accepted, then building a flow that triggers on it. Once the flow is active, recorded events trigger it asynchronously on the next poll cycle — see Custom Events → Triggering flows.
1. Define the event
Register a definition under Settings → Providers → Custom E-Com (click Add event) with this payload schema:
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"required": ["checkout_id", "customer_email", "cart_total", "currency", "abandoned_at"],
"properties": {
"checkout_id": { "type": "string" },
"customer_email": { "type": "string", "format": "email" },
"customer_phone": { "type": "string" },
"customer_first_name": { "type": "string" },
"customer_last_name": { "type": "string" },
"cart_total": { "type": "number" },
"currency": { "type": "string" },
"items": {
"type": "array",
"items": {
"type": "object",
"properties": {
"sku": { "type": "string" },
"title": { "type": "string" },
"quantity": { "type": "integer" },
"price": { "type": "number" }
}
}
},
"abandoned_at": { "type": "string", "format": "date-time" }
}
}2. Map identifiers
The identifier mapping tells LiveRecover which fields identify the shopper. For this schema:
{
"external_id_path": ["checkout_id"],
"email_path": ["customer_email"],
"phone_path": ["customer_phone"],
"first_name_path": ["customer_first_name"],
"last_name_path": ["customer_last_name"],
"received_at_path": ["abandoned_at"]
}external_id_path resolves a stable id for the abandoned checkout. The first email or phone we can resolve becomes the messaging channel; both is best.
3. Send the event
Sign the body with the brand's webhook secret (see HMAC Verification) and POST to the Custom E-Com endpoint:
SECRET='whsec_demo_secret'
URL='https://<your-vyg-webhook-host>/custom/<webhookId>'
BODY='{"type":"checkout_abandoned","event_id":"evt_chk_001","data":{"checkout_id":"ck_9f3a","customer_email":"jane@example.com","customer_phone":"+15551234567","customer_first_name":"Jane","customer_last_name":"Doe","cart_total":89.50,"currency":"USD","items":[{"sku":"COF-001","title":"Monthly Coffee","quantity":1,"price":29.99}],"abandoned_at":"2026-05-09T14:32:11Z"}}'
SIG=$(printf '%s' "$BODY" | openssl dgst -sha256 -hmac "$SECRET" -hex | awk '{print $NF}')
# expected: 259cf6883b6322233d897ce4f1279a12807b3a57e4ed64de0eb6ae9b49015a60
curl -sS -X POST "$URL" \
-H 'content-type: application/json' \
-H 'x-vyg-topic: checkout_abandoned' \
-H 'x-vyg-event-id: evt_chk_001' \
-H "x-vyg-signature: $SIG" \
--data "$BODY"A successful response (flow_event_id is always null — the event is recorded synchronously, while flow triggering runs asynchronously afterward):
{
"ok": true,
"custom_event_id": "8e3b0f5a-…-…",
"flow_event_id": null
}4. Verify end-to-end
After sending the request:
- The event appears under Settings → Providers → Custom E-Com: open the
checkout_abandoneddefinition and check its Events tab, which lists recent events (most recent first). - The contact (Jane Doe) is upserted with the email and phone you sent, so messaging targets the right shopper.
- If you have built and activated a flow on
checkout_abandoned, the recorded event triggers it asynchronously on the next poll cycle (typically within a few minutes) — see Custom Events → Triggering flows. Flow runs appear in the campaign's activity in the LiveRecover dashboard.
If you see 404 unknown_event_type, your type slug doesn't match a registered definition. If you see 422 missing_external_id or missing_contact_channel, recheck the identifier mapping against your real payload.