Vatly
Django

Manage owned subscriptions

Use the facade with a saved, authorized billable.

Use the facade with a saved, authorized billable. It checks local ownership before subscription mutations. A raw Vatly customer ID is never enough to authorize one.

Read current local subscriptions

from vatly_django.billing import Billing


def example(billable):
    return list(
        Billing().resources(billable, "subscription").values("vatly_id", "snapshot", "ends_at")
    )
Stored fieldMeaning
vatly_idSubscription identifier
snapshotFull Vatly resource using wire keys, including status
ends_atDerived expiry, not cancellation request time
entity_kind, entity_id, scopeHost ownership and tenant/storefront/mode

BillableMixin.subscribed() requires active/trial/on_grace_period status and no expired local end. It is a convenience policy, not your complete entitlement rule. Failed or stale synchronization must not be treated as proof of access.

Cancel or resume from a user action

These functions belong in authenticated, CSRF-protected POST handlers:

from vatly_django.billing import Billing


def example(billable):
    Billing().subscription_action(billable, "subscription_example", "cancel", immediately=False)
from vatly_django.billing import Billing


def example(billable):
    Billing().subscription_action(billable, "subscription_example", "resume")
ActionBehavior
cancelRequest cancellation; signed events update local state
resumeResume during grace and persist the API response
syncRefresh a subscription snapshot
swap, swap_and_invoice, swap_without_chargePlan-change policies; pass plan_id

The facade's action helper returns None. Use Fluent/SDK RequestOptions directly when your mutation workflow needs an explicit stable idempotency key.

Reconcile a known checkout

from vatly_django.billing import Billing


def example(billable):
    result = Billing().reconcile(billable, "checkout_example")
    # Schedule a bounded later reconcile while either tuple is not empty.
    return result.unresolved_line_ids, result.pending_subscription_ids

The facade checks the checkout's host record, performs bounded API reads and persists snapshots/links transactionally. This may hold the scope lock during reads; run it in a worker for busy deployments. It emits no created outbox event.

Signup lines reference their plan and name their subscription in relatedResources.subscriptionId; renewal lines reference the subscription as their product. Both link the subscription to the checkout's billable.

Result fieldMeaningWhat to do
subscriptionsLinked and stored subscriptionsNothing
pending_subscription_idsReferenced, but not readable yetReconcile again later
unresolved_line_idsSignup lines whose subscriptionId is still nullReconcile again later; it may never resolve
legacy_plan_idsPlan lines on historical orders without relatedResourcesResolve manually

A signed order.paid snapshot is frozen when it is sent and can carry a null subscriptionId, so the first order may still need an explicit reconcile. Schedule a bounded number of later reconciles while unresolved_line_ids is not empty. Never guess by customer/plan/quantity. Missing subscription IDs remain pending, not entitled. Trial flows may lack an order.paid event.

Grace expiry uses Fluent's shared mapper. canceledAt is the request time. Missing or ambiguous period deadlines raise and retain the prior committed state; monitor and reconcile instead of inventing an expiry. Active snapshots clear stale ends after resume. Fluent's guide describes this policy and its limits.

Subscription API reference

Copyright © 2026