LostChurn Docs
API Reference

Cancel Sessions

Cancel session API endpoints — create, query, advance, and complete cancel flow sessions at the edge.

Cancel flow sessions are managed via the Cloudflare edge worker. These endpoints create and drive cancel flow sessions stored in KV, with final outcomes persisted to SpacetimeDB on completion.

For background on how sessions work, see Building Flows > Session Management at the Edge.

Base URL

Cancel session endpoints use the edge worker base URL:

https://edge.lostchurn.com/api/cancel-session

Start a Session

Create a new cancel flow session for a subscriber.

POST /api/cancel-session/start

Request Body

FieldTypeRequiredDescription
merchant_idstringYesYour merchant UUID
customer_idstringYesThe customer's UUID
subscription_idstringYesThe subscription being canceled

Example

curl -X POST https://edge.lostchurn.com/api/cancel-session/start \
  -H "Content-Type: application/json" \
  -d '{
    "merchant_id": "m_abc123",
    "customer_id": "cus_def456",
    "subscription_id": "sub_ghi789"
  }'

Response

{
  "token": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "session": {
    "sessionId": "s_xyz789",
    "cancelFlowId": "cf_abc123",
    "merchantId": "m_abc123",
    "customerId": "cus_def456",
    "subscriptionId": "sub_ghi789",
    "currentStep": 1,
    "status": "in_progress",
    "responses": [],
    "acceptedOffer": null,
    "startedAt": "2026-03-13T10:00:00.000Z"
  }
}

The token is a UUID that serves as both the session identifier and access credential. Store it securely -- it is the only way to interact with this session.

The worker looks up the merchant's active cancel flow from SpacetimeDB when creating the session. If no active cancel flow is found for the merchant, the request returns a 404 error.

Get Session State

Retrieve the current session state and the configuration for the current step.

GET /api/cancel-session/:token

Path Parameters

ParameterTypeDescription
tokenstringThe session token returned by the start endpoint

Example

curl https://edge.lostchurn.com/api/cancel-session/a1b2c3d4-e5f6-7890-abcd-ef1234567890

Response

{
  "session": {
    "sessionId": "s_xyz789",
    "cancelFlowId": "cf_abc123",
    "merchantId": "m_abc123",
    "customerId": "cus_def456",
    "subscriptionId": "sub_ghi789",
    "currentStep": 1,
    "status": "in_progress",
    "responses": [],
    "acceptedOffer": null,
    "startedAt": "2026-03-13T10:00:00.000Z"
  },
  "step": {
    "stepType": "survey",
    "configJson": "{\"question\": \"Why are you canceling?\", \"options\": [...]}"
  }
}

The step object contains the step type and its JSON configuration, loaded from the cancel_flow_step table in SpacetimeDB. If the step configuration cannot be loaded, the step field is null but the session is still returned.

Error Responses

StatusMeaning
404Session not found or expired (sessions expire after 1 hour of inactivity)

Advance to Next Step

Record the subscriber's response to the current step and advance to the next one.

POST /api/cancel-session/:token/advance

Path Parameters

ParameterTypeDescription
tokenstringThe session token

Request Body

FieldTypeRequiredDescription
response_jsonstringNoJSON-encoded response data from the current step
step_typestringNoThe type of step being completed (e.g., survey, offer)

Example

curl -X POST https://edge.lostchurn.com/api/cancel-session/a1b2c3d4-e5f6-7890-abcd-ef1234567890/advance \
  -H "Content-Type: application/json" \
  -d '{
    "response_json": "{\"reason\": \"too_expensive\"}",
    "step_type": "survey"
  }'

Response

{
  "session": {
    "sessionId": "s_xyz789",
    "currentStep": 2,
    "status": "in_progress",
    "responses": [
      {
        "stepType": "survey",
        "responseJson": "{\"reason\": \"too_expensive\"}",
        "acceptedOffer": null
      }
    ]
  }
}

Each advance appends the response to the session's responses array and increments currentStep. If the response_json contains an accepted_offer field, it is extracted and stored on the session.

Error Responses

StatusMeaning
404Session not found or expired
400Session has already been completed

Complete Session

Finalize the session with an outcome. This persists the result to SpacetimeDB and deletes the KV session.

POST /api/cancel-session/:token/complete

Path Parameters

ParameterTypeDescription
tokenstringThe session token

Request Body

FieldTypeRequiredDescription
outcomestringYesEither saved (subscriber retained) or churned (subscriber canceled)
reasonstringYesPrimary cancellation reason (e.g., too_expensive, missing_features)
sub_reasonstringNoSecondary reason for more granular reporting
free_textstringNoFree-text feedback from the subscriber

Example

curl -X POST https://edge.lostchurn.com/api/cancel-session/a1b2c3d4-e5f6-7890-abcd-ef1234567890/complete \
  -H "Content-Type: application/json" \
  -d '{
    "outcome": "churned",
    "reason": "too_expensive",
    "sub_reason": "budget_cuts",
    "free_text": "Love the product but we had to cut costs."
  }'

Response

{
  "completed": true,
  "outcome": "churned",
  "session_id": "s_xyz789"
}

What Happens on Completion

  1. The session status is updated to the specified outcome (saved or churned)
  2. The worker calls the complete_cancel_session_from_kv reducer in SpacetimeDB, which:
    • Inserts a cancel_feedback row with the full response history
    • Emits a recovery_event (either CancelFlowCompleted or CancelFlowSaved)
    • Updates subscription status if the outcome is churned
  3. The KV session record is deleted

Once a session is completed, the token is invalidated and cannot be used again. If the SpacetimeDB persistence fails, the session is still consumed from KV -- the completion is logged for retry via a dead-letter mechanism.

Error Responses

StatusMeaning
400Missing required fields (outcome, reason) or invalid outcome value
404Session not found or expired

Session Lifecycle Summary

POST /start     → token issued, session created in KV (status: in_progress)
GET  /:token    → read session state + current step config
POST /:token/advance  → record response, move to next step
POST /:token/complete → persist outcome to DB, delete KV record
                         (or session auto-expires after 1 hour)

Next Steps

  • Building Flows -- visual flow builder and session architecture
  • Measuring Saves -- analyze cancel flow performance
  • Webhooks -- receive customer.subscription_canceled and customer.retained events

On this page