Skip to main content

Installation

Add the snippet to every authenticated page — typically inside your app shell’s <body>, after the user is confirmed to be logged in:
<script
  src="https://api.gatlio.io/v1/enforce.js"
  data-tenant-slug="acme"
  data-publishable-key="pk_live_abc123"
  data-customer-id="cus_xxxxxxxxxxxxxxxx"
  data-hmac="SERVER_GENERATED_HMAC"
></script>
On load, the snippet fetches the subscriber’s billing status and renders the appropriate UI. Render this tag server-side — data-hmac must be generated on the server using your HMAC secret, never in client-side code.

Attributes

AttributeRequiredDescription
data-tenant-slugYour Gatlio tenant slug, visible in the dashboard
data-publishable-keyYour Gatlio publishable key (pk_live_… or pk_test_…)
data-customer-idStripe customer ID (cus_…) for auto-init. If omitted, call window.Gatlio.check(cid, hmac) manually
data-hmacHMAC-SHA256(hmacSecret, stripe_customer_id) — generated server-side. Strongly recommended in production to prevent customer ID spoofing
data-api-baseBase URL of your Gatlio API. Defaults to the Gatlio-hosted API
data-gate-baseOrigin for the gate iframe. Defaults to data-api-base. Must be a Gatlio-controlled origin
data-localeUI locale — en, fr, es, de. Defaults to navigator.language, fallback en

Manual init (SPAs)

If your app renders the subscriber’s customer ID after the initial page load (e.g. after an auth redirect), omit data-customer-id and call window.Gatlio.check() manually once the ID is available:
// After the user is authenticated and you have their Stripe customer ID and HMAC:
window.Gatlio.check('cus_xxxxxxxxxxxxxxxx', 'server_generated_hmac')
Call Gatlio.check() again any time the signed-in user changes (e.g. account switcher). Always pass the HMAC alongside the customer ID.

Enforcement behaviour

Active

No UI is shown. The subscriber’s session continues unaffected.

Warning (soft decline)

A dismissable amber banner appears fixed at the top of the viewport:
  • Includes a localized message and an Update card CTA if card_update_url is configured in your dashboard
  • The subscriber can dismiss the banner, but it reappears on the next page load
  • Polling continues every 10 minutes so the banner disappears automatically once the issue resolves

Lockout (hard decline or exhausted retries)

A full-screen iframe overlay covers the entire viewport:
  • z-index: 2147483647 — rendered above all app content
  • sandbox="allow-scripts allow-same-origin allow-popups allow-forms" — the gate page cannot access parent DOM or storage
  • Contains a Stripe Payment Element for immediate card update
  • Dismisses automatically once the subscriber completes the card update flow

Polling

After the initial check, the snippet polls the status API every 10 minutes while the tab is visible. Polling pauses when the tab is hidden and resumes on visibility. Once a subscriber enters lockout, polling stops and the gate page takes over. After recovery, the snippet goes idle for the remainder of the session.

Double-load protection

The snippet is guarded against being loaded more than once per page. If the script tag is injected multiple times (e.g. via a tag manager), only the first load takes effect.