Skip to main content
Grantiva can ingest your app’s own subscription state — from Apple In-App Purchase and/or your Stripe account — and surface it as a managed subscription object inside custom_claims in every attestation JWT. Your backend gates features by verifying the signed JWT it already receives; there is no entitlement API to call and no API key involved.
Subscription entitlement ingestion is an Enterprise feature.

Sharing units (families)

Entitlements attach to a Subject — an app-owned sharing unit such as a family or household, not an individual device. One paying member entitles every device in the unit, because every member’s device declares the same sharing-unit id at attestation time. The join key is one opaque id used in three places — all three must carry the identical value:
Apple’s appAccountToken requires a UUID, so your sharing-unit id must be a UUID string.
The Subject id is opaque to Grantiva — mint a stable UUID per household in your own system and reuse it everywhere. It is distinct from identify(_:), which scopes the individual user for feedback and flag targeting.

The claim

The claim appears under custom_claims.subscription in the decoded JWT:
Rules worth knowing:
  • Absent means free. If the device’s sharing unit has no entitlement, the subscription key is simply not present.
  • Gate on is_active, not on status — it already accounts for grace periods.
  • When a unit holds multiple entitlements (two payers, or Apple + Stripe), the JWT surfaces the best one: active-like first, then higher tier, then later expiry.
  • subscription is a reserved claim key, managed by Grantiva. Attempts to create a custom claim named subscription are rejected, and the managed claim is injected even if your custom-claims toggle is off.
  • The JWT has a TTL of at most 1 hour and refreshes on every attest/refresh, so the claim self-heals — treat the JWT as the source of truth.
Read subscription from the decoded JWT. The flat customClaims map in the validate/refresh HTTP response stringifies nested objects and is not suitable for structured claims.

How state flows in

Apple and Stripe push subscription lifecycle events directly to Grantiva:
Grantiva resolves the sharing unit from the appAccountToken / client_reference_id, maps the product or price to a tier using your entitlement configuration, updates the entitlement, and from then on projects the claim into every JWT minted for any device in that unit. Ingestion trusts source events; there is no periodic re-sync against the App Store Server API or Stripe API in v1.

Instant invalidation webhooks

JWTs live up to an hour, so Grantiva also fires outbound webhooks the moment an entitlement changes: subscription.changed (always), subscription.expired, and subscription.refunded. Use them as a cache-bust nudge — evict whatever you cached for that subject_id and re-read the next JWT. See Webhooks for payloads and signature verification.

Setup checklist

  1. Configure your product/price → tier mapping (Enterprise, dashboard admin).
  2. Point Apple App Store Server Notifications v2 and/or your Stripe webhook at Grantiva.
  3. In your app: mint a household UUID, call setSubjectId, and pass the same UUID at purchase time.
  4. In your backend: verify the JWT against Grantiva’s JWKS and gate on custom_claims.subscription.is_active.
Or follow the end-to-end Subscription Claims Quick Start.