API Documentation
Use the Saveloops API to fetch offers, record outcomes, apply offers to Stripe, analyze sentiment, and manage dunning and success signals.
Base URL & authentication
All endpoints use the base URL {your-app}/api/v1. Send your organization API key in the X-Knot-Key header. Requests and responses are JSON.
X-Knot-Key: <organization_api_key>
POST /api/v1/intercept
Fetch offers and flags for the current context (e.g. cancel flow). Use this when the user hits a cancel or exit intent so you can show the right offer or prompt.
Request body
{
"target_id": "cancel-btn", // required, string, max 255
"user_email": "user@example.com", // optional, email
"trigger": "click", // optional: click | exit_intent | hover
"stripe_customer_id": "cus_xxx", // optional
"stripe_subscription_id": "sub_xxx", // optional
"mrr_value": 29 // optional, number >= 0
}
Response (200)
{
"offers": [
{ "id": 1, "type": "discount", "value": 20, "label": "20% off", "config": {}, "experiment": null }
],
"show_update_card": false,
"show_onboarding_prompt": false
}
POST /api/v1/intercept/outcome
Record whether the user saved (accepted an offer) or abandoned (closed without accepting). Call this after the user makes a choice in the intercept flow.
Request body
{
"offer_id": 1, // required, integer, exists in offers
"status": "saved", // required: saved | abandoned
"user_email": "user@example.com", // optional
"mrr_value": 29 // optional, number >= 0
}
Response (200)
{ "message": "Recorded" }
POST /api/v1/intercept/apply-offer
Apply the selected offer to the Stripe subscription (e.g. apply discount). Records a save in analytics on success.
Request body
{
"offer_id": 1, // required, integer, exists in offers
"stripe_subscription_id": "sub_xxx", // required
"user_email": "user@example.com", // optional
"mrr_value": 29 // optional, number >= 0
}
Response (200)
{ "message": "Offer applied successfully." }
On failure (e.g. Stripe error): 422 with message and optional error (when debug is on).
POST /api/v1/intercept/sentiment
Analyze the user’s cancellation reason and get a suggested action (e.g. discount, setup call, support ticket) plus matching offers. May create a support ticket when suggested action is support.
Request body
{
"reason": "Too expensive for now", // required, string, max 2000
"user_email": "user@example.com", // optional
"target_id": "cancel-form" // optional, string, max 255
}
Response (200)
{
"suggested_action": "discount",
"confidence": 0.85,
"offers": [ { "id": 1, "type": "discount", "value": 20, "label": "20% off", "config": {}, "experiment": null } ],
"create_support_ticket": false
}
POST /api/v1/dunning
Mark an end user as needing payment update (e.g. after Stripe invoice.payment_failed). Call from your backend when you detect a failed payment. One of user_email or stripe_customer_id is required.
Request body
{
"user_email": "user@example.com", // required without stripe_customer_id
"stripe_customer_id": "cus_xxx" // required without user_email
}
Response (200)
{ "message": "Recorded" }
POST /api/v1/success-signal
Record a positive event (e.g. payment succeeded, subscription renewed). Upserts the end user and sets last success time. One of user_email or stripe_customer_id is required.
Request body
{
"event": "payment_succeeded", // required, string, max 64
"user_email": "user@example.com", // required without stripe_customer_id
"stripe_customer_id": "cus_xxx" // required without user_email
}
Response (200)
{ "message": "Recorded", "end_user_id": 123 }
Example request
curl -X POST "https://your-app.com/api/v1/intercept" \
-H "Content-Type: application/json" \
-H "X-Knot-Key: your_organization_api_key" \
-d '{"target_id":"cancel-btn","user_email":"user@example.com"}'