ripllo.abandoned_cart.recovered.v1
Fires when a previously-abandoned cart is recovered — the buyer came back and completed a checkout that originated from a reminded cart. The primary metric for "did the recovery email pay off".
When it fires
When POST /api/v1/abandoned-cart/recover sets recoveredAt on a reminder row for the first time. The endpoint is idempotent on (accountId, checkoutSessionId), so retried partner-platform webhooks don't re-emit.
If the partner platform calls /recover for a checkout that has no matching reminder (the cart was never abandoned, or never reminded), no event fires — the recovery endpoint responds with recovered: false and Ripllo is silent.
Payload
{
"id": "evt_01HX...",
"type": "ripllo.abandoned_cart.recovered.v1",
"createdAt": "2026-05-13T10:43:22.187Z",
"accountId": "acc_01HX...",
"data": {
"reminder": {
"id": "acr_01HX...",
"accountId": "acc_01HX...",
"customerId": "cus_01HX...",
"cartId": "cart_storlaunch_19823",
"email": "alice@example.com",
"valueAtSend": 250000,
"currencyAtSend": "IDR",
"discountCodeId": "dc_01HX...",
"status": "delivered",
"sentAt": "2026-05-13T06:42:00.000Z",
"recoveredAt": "2026-05-13T10:43:22.140Z",
"externalSource": "storlaunch",
"externalRef": "cart_storlaunch_19823"
},
"checkoutSessionId": "cs_storlaunch_01HX...",
"timeToRecoveryMs": 14482140
}
}
The timeToRecoveryMs is a convenience: the milliseconds between sentAt and recoveredAt. Useful for histograms without doing date math in your handler.
Handler examples
// Node
if (event.type === 'ripllo.abandoned_cart.recovered.v1') {
const { reminder, checkoutSessionId, timeToRecoveryMs } = event.data;
analytics.track('cart_recovered', {
customerId: reminder.customerId,
valueIdr: reminder.valueAtSend,
discountUsed: reminder.discountCodeId !== null,
hoursToRecovery: Math.round(timeToRecoveryMs / 3_600_000),
checkoutSessionId,
});
}
# Python
if event['type'] == 'ripllo.abandoned_cart.recovered.v1':
reminder = event['data']['reminder']
analytics.track('cart_recovered',
customer_id=reminder['customerId'],
value_idr=reminder['valueAtSend'],
discount_used=reminder['discountCodeId'] is not None,
hours_to_recovery=round(event['data']['timeToRecoveryMs'] / 3_600_000),
)
// Go
if event.Type == "ripllo.abandoned_cart.recovered.v1" {
var data struct {
Reminder ripllo.AbandonedCartReminder `json:"reminder"`
CheckoutSessionID string `json:"checkoutSessionId"`
TimeToRecoveryMs int64 `json:"timeToRecoveryMs"`
}
_ = json.Unmarshal(event.Data, &data)
analytics.Track("cart_recovered", data.Reminder.CustomerID, data.Reminder.ValueAtSend, data.TimeToRecoveryMs)
}
What to do
- Update recovery analytics. This event is the single source of truth for the recovery rate. Aggregate by
discountCodeId !== nullto compare bundled-discount vs no-discount recovery. - Optionally thank the customer. Sending a "thanks for coming back!" follow-up can deepen engagement, but tread carefully — too many emails will provoke an unsubscribe.
- Compute true recovery value. Compare
reminder.valueAtSend(cart size at abandonment) to the actual checkout total (from the partner platform's order data) — the buyer may have added or removed items between abandonment and recovery. - Stop any "second reminder" sequences. If you've layered automations on top of Ripllo's first reminder, this is the cancel signal.
Common pitfalls
- Counting
valueAtSendas recovered revenue. It's the cart-snapshot value at abandonment time, not the final checkout total. The two diverge if the buyer added or removed items. For revenue reporting, use the partner platform's order-completed data and cross-reference bycheckoutSessionId. - Treating recovery as instant. The buyer might come back days later.
timeToRecoveryMscan easily span hours or days; bucket appropriately when reporting. - Assuming the reminder caused the recovery. Attribution is "the buyer was reminded and then completed a checkout". Some of those buyers would have come back anyway. The honest measure is incremental recovery rate vs a holdout group, which Ripllo doesn't currently A/B-test for you — you'd need to design that on the partner-platform side.
- Sending the recovery event back to the partner. This event is for you (the integration owner) to track. Don't bounce it back to the partner platform — the partner already knows the checkout completed.
Related events
ripllo.abandoned_cart.reminder_sent.v1— reserved-not-emitted; the "we sent the email" counterpart.ripllo.abandoned_cart.suppressed.v1— reserved-not-emitted; buyer opted out.ripllo.discount_code.redeemed.v1— if the recovery used the bundled discount code, this event fires too.
Next
- Abandoned cart resource — the full config + recovery API.
- Webhooks reference — envelope, retries, signature verification.