CyccleDocs

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. 1

    Merchant server

    Mints a short-lived, origin-bound launch token.

  2. 2

    Merchant browser

    Loads the v1 script and opens Cyccle with the opaque token.

  3. 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.

HTML
<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.

Server-side TypeScript
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.

Client-side TypeScript
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.

HTTP response header
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

  1. Use a Stripe test customer and the exact subscription intended for the session.
  2. Confirm the request to Cyccle is made server-side and no API key appears in browser assets or network requests.
  3. Open and close the flow with keyboard controls and verify focus returns to the launch control.
  4. Force a loader or token failure and confirm the native cancellation path remains available.
  5. Verify sibling subscriptions are unchanged and refresh billing state from the server after completion.