App API Routes
CRM HTTP routes for subscription list, cancel, and winback—authentication, request data, and responses.
Paths below are relative to your CRM deployment origin (the same host as the dashboard)—for example https://your-crm.example.com/api/subscriptions.
These routes support end-user flows on your site (typically with Piano): list Stripe subscriptions, cancel at period end, or apply a winback price during cancellation.
Authentication
| Requirement | Details |
|---|---|
Query token | Piano JWT for the signed-in user. Payload must include aud (Piano application id) and sub (Piano user id). |
Header Origin | Browser Origin matching the publication’s configured origin. Used with aud to resolve the publication and Stripe accounts. |
Missing or invalid values return 400 with { "error": "..." }. Winback also calls Piano token verification; failure returns 401.
Configure winback offers in publication Settings (Smart actions) before calling winback from production.
GET /api/subscriptions
List Stripe subscriptions for the Piano user behind token.
Request
| Part | Required |
|---|---|
Origin header | Yes |
token query | Yes |
Response 200
{
"subscriptions": [ /* Stripe subscription objects */ ]
}Each item is a Stripe Subscription, plus:
| Field | Type | Description |
|---|---|---|
stripeAccountId | string | null | Stripe account id when multiple accounts exist |
stripeAccountName | string | null | Account title from CRM |
May include expanded schedule when subscription schedules apply.
Errors 400
Origin not found., Token not found., Invalid token - no aud., Publication not found., Piano user not found., CRM Stripe accounts not found.
DELETE /api/subscriptions/{stripeSubscriptionId}
Schedule cancellation at end of current billing period (cancel_at_period_end: true). Releases an attached Stripe subscription schedule first when present.
Request
| Part | Required |
|---|---|
Origin header | Yes |
token query | Yes |
Path stripeSubscriptionId | Yes (e.g. sub_...) |
Response 200
{
"success": true,
"data": { /* updated Stripe subscription */ }
}Errors 400
Auth/publication errors as above; Subscription not found., Failed to cancel subscription., Failed to release subscription., Failed to get Stripe client for this publication.
POST /api/subscriptions/winback
Apply a winback Stripe Price when the user accepts an alternate plan in your cancellation UI.
Request
| Part | Required |
|---|---|
Origin header | Yes |
token query | Yes (verified with Piano) |
JSON body:
| Field | Type | Required | Description |
|---|---|---|---|
subscriptionId | string | Yes | Stripe subscription id |
newPriceId | string | Yes | Stripe Price id for the winback offer |
{
"subscriptionId": "sub_1Example",
"newPriceId": "price_1WinbackOffer"
}Response 200 (no schedule template for price)
{
"data": {
"message": "Plan changed. No schedule for this price."
},
"error": null
}Response 200 (schedule at period end)
{
"success": true,
"message": "Subscription plan will change at the end of your current billing period",
"data": {
"subscriptionId": "sub_1Example",
"currentPeriodEnd": 1735689600,
"newPriceId": "price_1WinbackOffer"
}
}Errors
| Status | When |
|---|---|
| 400 | Missing auth, body, publication, user, or subscription; Stripe client unavailable |
| 401 | Invalid token. |
| 500 | Plan change or schedule failure (may include correlation id) |
Typical cancellation flow
sequenceDiagram
participant User as Subscriber
participant Site as Your site
participant CRM as CoEditor API
User->>Site: Start cancellation
Site->>CRM: GET /api/subscriptions?token=...
CRM-->>Site: subscriptions
alt Winback accepted
Site->>CRM: POST /api/subscriptions/winback
Note over Site,CRM: subscriptionId, newPriceId
else Cancel
Site->>CRM: DELETE /api/subscriptions/{id}?token=...
endNot covered here
Auth, Google mailbox, AI, webhooks, and other internal CRM routes are documented in engineering docs. This page lists routes customer integrations call from public cancellation and account flows.