Runtime v1
Install Cyccle
Connect your authenticated cancellation entry point to Cyccle’s hosted flow while keeping identity, billing credentials, and fallback control on your server.
How the integration works
Your backend identifies the signed-in customer and exact subscription, then exchanges a private workspace API key for a short-lived launch token. The browser gives only that opaque token to the Cyccle runtime.
- 1
Merchant server
Mints a short-lived, origin-bound launch token.
- 2
Merchant browser
Loads the v1 script and opens Cyccle with the opaque token.
- 3
Cyccle runtime
Resolves the published flow and commits approved actions server-side.
The iframe and loader validate their exact peer origin. Billing actions are revalidated and committed server-side; browser callbacks only tell your interface when to refresh.
Prerequisites
Before changing application code, confirm that you have:
- A Cyccle workspace API key stored in a server-side secret manager.
- The exact application origin registered with Cyccle, including scheme and host but no path.
- A signed-in customer and subscription boundary in the existing application.
- A native, subscription-scoped cancellation action that remains available as fallback.
1. Load the runtime
Load the versioned script asynchronously after the host application becomes interactive.
<script async src="https://flow.cyccle.co/v1/cyccle.js"></script>In Next.js 16, use next/script with strategy="afterInteractive" and do not enable the launch control until the script’s onLoad callback fires.
2. Mint a launch token on your server
Create the token only after authorizing the signed-in user against the requested customer and subscription. Use a configured canonical origin; do not trust a forwarded request header as the origin policy.
const response = await fetch("https://api.cyccle.co/v1/session-tokens", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.CYCCLE_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
customerId,
subscriptionId,
stripeMode: "test",
origin: process.env.CYCCLE_APP_ORIGIN,
}),
cache: "no-store",
});
const { token: launchToken, branding } = await response.json();Launch tokens expire within 15 minutes and are single-use. Return only token, expiresAt, and branding to the browser.
3. Open the cancellation flow
Call the browser API from the existing cancellation control once the loader and launch token are ready.
await window.cyccle.open({
launchToken,
branding,
onComplete() {
// Refresh billing state from your server.
},
onError() {
// Restore the native, subscription-scoped cancellation path.
},
});Refresh subscription state from your server after completion. Never use a browser callback as proof that a billing mutation succeeded.
4. Update Content Security Policy
Add the exact runtime origin to the existing directives while preserving every current policy entry.
Content-Security-Policy:
script-src 'self' https://flow.cyccle.co;
style-src 'self' https://flow.cyccle.co;
frame-src https://flow.cyccle.co;Do not add wildcard sources or unsafe-inline for Cyccle. The hosted frame separately restricts frame-ancestors to your registered origin.
5. Test and verify
- Use a Stripe test customer and the exact subscription intended for the session.
- Confirm the request to Cyccle is made server-side and no API key appears in browser assets or network requests.
- Open and close the flow with keyboard controls and verify focus returns to the launch control.
- Force a loader or token failure and confirm the native cancellation path remains available.
- Verify sibling subscriptions are unchanged and refresh billing state from the server after completion.