Docs

RetentBase API integration guide

End-to-end setup instructions for Hosted Flow and Advanced API integration.

Before you start

Public docs intentionally avoid workspace-specific secrets and identifiers. Create a workspace first, then use /dashboard/docs to access your actual workspaceKey and Signing Secret.

Hosted helper API POST /api/v1/cancel-link is available on Core and Advanced. Session diagnostics (GET /api/v1/cancel-sessions/:id) and custom cancellation ingestion/update APIs require Advanced.

Choose integration model

Call the RetentBase API at https://api.retentbase.com. If you use the hosted cancellation flow, redirect customers to the cancel host at https://cancel.retentbase.com.

Plan model: Core = Hosted flow only. Advanced = Hosted flow or Custom API + advanced analytics.

Hosted flow: complete implementation

In this model, your backend calls RetentBase once to create a hosted cancellation link, then redirects the customer. RetentBase never changes billing directly.

Required (2 steps)

  1. Create link via POST /api/v1/cancel-link.
  2. Redirect the user to the returned url.

This is all you need on the Core plan. Hosted flow works without any Advanced fields.

Optional but recommended: verify ci_result_token before changing billing state. The token is returned in the URL hash fragment (#ci_result_token=...).

Advanced only: you can optionally send context.mrr and context.tenure for segmentation. If omitted, hosted flow still works normally.

Final schema: userId, context, metadata, and environment. On the Core plan, context.mrr and context.tenureDays can be omitted.

Step 1: Create cancel link from your backend

POST /api/v1/cancel-link

const response = await fetch("https://api.retentbase.com/api/v1/cancel-link", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    Authorization: "Bearer " + process.env.RETENTBASE_API_KEY,
  },
  body: JSON.stringify({
    userId: "user_123",
    returnUrl: "https://app.example.com/billing/cancel/callback",
    environment: "prod",
    context: {
      plan: "pro_monthly",
      // Advanced only:
      mrr: 129,
      tenureDays: 42,
    },
    metadata: {
      source: "billing_settings_page",
    },
  }),
});

if (!response.ok) throw new Error("cancel-link failed");
const payload = await response.json();
// payload.url, payload.expiresAt, payload.environment, payload.sessionId

Step 2: Redirect user to hosted page

Redirect

return Response.redirect(payload.url, 302);

Optional step: Verify result token before billing action

Verify ci_result_token

// 1) In your callback page (client-side), read token from URL fragment.
const token = new URLSearchParams(window.location.hash.replace(/^#/, "")).get("ci_result_token");
if (!token) throw new Error("Missing ci_result_token");

// 2) Send token to your backend so billing actions stay server-side.
const res = await fetch("/api/billing/cancel-callback", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ token }),
});
if (!res.ok) throw new Error("Callback processing failed");
POST/api/v1/cancel-link

Creates a hosted-flow URL with a short-lived token and a session_id for diagnostics.

Notes

  • Use user_id as your stable customer reference.
  • Use context only for analytics enrichment; the hosted flow works without MRR or tenure.

Availability

Core and Advanced plans

Auth

Authorization: Bearer <Workspace API Key> or x-api-key

Content-Type

application/json

JSON Body

FieldTypeRequiredDescription
userIdstringYesYour stable user identifier.
returnUrlstring(url)NoOverride callback URL. Must be allowlisted.
environment"prod" | "sandbox"NoEnvironment for the hosted session.
context.planstringNoCustomer plan label for analytics context.
context.mrrnumberNoOptional (Advanced): monthly recurring revenue for segmentation cohorts.
context.tenureDaysintegerNoOptional (Advanced): customer tenure in days for lifecycle cohorts.
metadataobjectNoOptional flat object (string/number/boolean/null values).

Success Responses

StatusMeaning
200Returns hosted URL, expires_at, environment and session_id.

Error Responses

StatusMeaning
400Invalid body or return_url not allowlisted.
401Missing/invalid API key.
402Workspace is read-only due to expired subscription access.
403Forbidden: this endpoint is unavailable for the current workspace or plan.
409Default return URL is not configured.
415Content-Type must be application/json.
429Rate limit exceeded.

Optional hardening endpoints

POST/api/public/cancel/result

Optional: verifies the signed hosted-flow result token and returns the trusted outcome payload.

Availability

Optional hardening (Core and Advanced plans)

Auth

No API key. Token signature validation is required.

JSON Body

FieldTypeRequiredDescription
tokenstringYesThe ci_result_token received in your callback URL hash fragment.

Success Responses

StatusMeaning
200Token valid and result payload returned.

Error Responses

StatusMeaning
400Missing or malformed token.
401Expired token.
GET/api/v1/cancel-sessions/{session_id}

Returns hosted-session diagnostics (session state, linked event, completion status).

Availability

Advanced plan only

Auth

Authorization: Bearer <Workspace API Key> or x-api-key

Path Parameters

FieldTypeRequiredDescription
session_idstring(uuid)YesThe session_id from cancel-link response or rb_session callback param.

Success Responses

StatusMeaning
200Session status returned.

Error Responses

StatusMeaning
400Missing session_id.
401Missing or invalid API key.
403Forbidden: this endpoint is unavailable for the current workspace or plan.
404Session not found for this workspace.
429Rate limit exceeded.

Implementation checklist

  • Store workspace API keys and any result-token verification helpers only in server-side code.
  • Use stable `eventId` values to guarantee idempotent retries.
  • Use sandbox mode (`environment: "sandbox"`) before enabling production traffic.
  • Sandbox traffic stays isolated from retention health, weekly issue detection, and scheduled report emails.
  • Set Stripe webhook to `POST /api/stripe/webhook` and include `checkout.session.completed`, `customer.subscription.*`, `invoice.paid`, and `invoice.payment_failed`.
  • Use the dashboard billing surface for checkout and Stripe portal access instead of hard-coding plan changes.
  • Monitor `GET /api/health?deep=1` (authorized) for Stripe, notifications, and webhook pipeline health.
  • Log non-2xx responses with status + body to speed up integration debugging.