Subscription Plans
Charge your app's users a recurring price through Hypery and give each payment credit grants that can only be spent in your app — price and grants configured independently.
Overview
Subscription plans let your app sell a recurring subscription (a monthly price, plus an optional annual price) and give subscribers credit grants to spend on AI inside your app.
- You're the merchant of record. Payments go to your Stripe Connect account. Hypery takes its platform fee.
- Price and grants are separate. A plan can charge $10/month and grant $5, $25, several grants of different types, or nothing at all.
- Grants stay in your app. A grant can only pay for requests made through your app. Other apps, and the user's own direct API usage, can't spend it.
- Grants expire and never roll over. Subscribers get fresh grants every month, and each grant expires after the number of days you set.
- Annual billing, monthly grants. Annual subscribers pay once a year but still receive the plan's grants every month, so credits never land all at once.
- Usage keeps working when a grant runs out. Once a grant is used up or expired, the user's usage bills the normal way (their balance or card).
- No re-entering a card. Subscribing uses a card the user already gave Hypery: their wallet card, or the card they added when they set up billing.
Authentication:
- Plan management: your personal API key (
ak_...) or a session/OAuth token withwrite(readfor GET). Only the app's owner or a billing admin can manage plans. - Subscribing: an OAuth token for your app with the
billing:chargescope.
Plans require Stripe Connect onboarding to be complete for the app
(chargesEnabled). Until then, creating a plan returns 409 MARKETPLACE_NOT_ONBOARDED.
Grant types
Every grant on a plan is one of two types:
How each type is spent:
- Hypery grants:
- A request is paid from the grant first; anything left over is charged to the user's balance.
- A request normally billed through Vercel AI Gateway is routed through the credit ledger instead, but only when the grant covers the whole request's estimate.
- Hypery grants don't cover
/v1/ai(the Vercel AI Gateway protocol endpoint) or predictions.
- Stripe grants:
- While the grant has value left, your app's metered usage is billed to the dedicated customer, where the credit grant offsets it.
- Once it's spent, usage goes back to the user's regular Stripe customer.
Funding: you pay for every grant.
- When a grant is issued, its USD value is debited from your team's Hypery balance.
- If the balance can't cover it, the amount is added to your team's Stripe invoice instead.
- If neither works, the grant is marked
funding_failed(not spendable) and retried automatically.
Plans
List plans
Endpoint: GET /api/apps/{appId}/plans
curl https://hypery.ai/api/apps/{appId}/plans \
-H "Authorization: Bearer ak_your_key"{
"success": true,
"data": {
"connectReady": true,
"plans": [
{
"_id": "66f…",
"name": "Pro",
"status": "active",
"priceCents": 1000,
"interval": "month",
"prices": [
{ "interval": "month", "priceCents": 1000, "stripePriceId": "price_…", "active": true },
{ "interval": "year", "priceCents": 10000, "stripePriceId": "price_…", "active": true }
],
"grants": [
{ "id": "g_1a2b…", "type": "hypery", "amountUsd": 10, "expiresAfterDays": 31, "issueOn": "each_payment" }
],
"activeSubscribers": 42,
"economics": {
"priceUsd": 10, "monthlyPriceUsd": 10, "annualPriceUsd": 100, "annualEffectiveMonthlyUsd": 8.33,
"annualSavingsPct": 17, "recurringGrantUsd": 10, "firstPaymentGrantUsd": 10, "grantsExceedPrice": true
}
}
]
}
}Create a plan
Endpoint: POST /api/apps/{appId}/plans
curl -X POST https://hypery.ai/api/apps/{appId}/plans \
-H "Authorization: Bearer ak_your_key" \
-H "Content-Type: application/json" \
-d '{
"name": "Pro",
"monthlyPriceCents": 1000,
"annualPriceCents": 10000,
"grants": [
{ "type": "hypery", "amountUsd": 10, "expiresAfterDays": 31, "issueOn": "each_payment" },
{ "type": "hypery", "amountUsd": 5, "expiresAfterDays": 7, "issueOn": "first_payment_only" }
]
}'plans[].priceCents / stripePriceId / interval mirror the monthly price, for
older clients; read prices[] for both.
Update a plan
Endpoint: PATCH /api/apps/{appId}/plans/{planId}
All fields are optional: name, description, monthlyPriceCents,
annualPriceCents, grants, status (active / archived).
- Price changes create a new Stripe price. Existing subscribers on that interval move to it at their next renewal, with no proration.
annualPriceCents: nullremoves the annual option for new subscribers. Existing annual subscribers keep renewing until they cancel or switch.monthlyPriceCents: null(only when an annual price exists) makes the plan annual-only. Existing monthly subscribers keep renewing; clients that don't sendintervalget400 INTERVAL_NOT_OFFERED, so update them to send"year".- Grant changes apply from the next grant issued (including the next
monthly grant of annual subscribers). Grants already issued
keep their original amount and expiry.
- To keep an existing grant, send its
idback. - A grant without an
idis treated as new. - A grant you leave out is removed.
- To keep an existing grant, send its
- Archiving hides the plan from new subscribers. Existing subscriptions keep renewing until they're canceled.
Plan details
Endpoint: GET /api/apps/{appId}/plans/{planId}
Returns the plan, its economics, and grant totals grouped by status: grants issued, credits issued, used, and remaining.
Subscribing (from your app)
With @hyperyai/sdk, useAppSubscription(appId)
and <SubscribeButton planId interval> handle
the whole flow by opening Hypery's hosted subscribe page as a popup (or a new
tab / redirect on mobile). The endpoints below are what they call.
Subscriptions belong to a team. On the hosted page the user explicitly picks which Hypery team the subscription is for. Only that team's owner or admin can subscribe it, the team's card is charged (a card can be added right there), and a team has at most one live subscription per plan. Credits go to the team and can be used by its members in your app. Team owners and admins manage the subscription from the team's Billing page in Hypery, or through the endpoints below.
Start a hosted subscribe session
Endpoint: POST /api/marketplace/subscribe-sessions (scope billing:charge, OAuth token for your app)
curl -X POST https://hypery.ai/api/marketplace/subscribe-sessions \
-H "Authorization: Bearer <user OAuth token>" \
-H "Content-Type: application/json" \
-d '{ "planId": "66f…", "interval": "year", "returnOrigin": "https://yourapp.com", "state": "c0ffee-8f3a1" }'Returns 201 { sessionId, url, expiresAt }. Open url (valid for 30 minutes). The
session is bound to the first Hypery account that opens it.
On the page the user signs in (and passes 2FA) if needed, picks a team they can bill, adds a card to that team if it has none, picks monthly or yearly, and confirms. 3-D Secure happens in the page.
When it finishes:
- Popup: the page posts
{ type: "hypery:subscribe", sessionId, state, status: "completed" | "cancelled", teamId, subscriptionId }toreturnOrigin(sender origin ishttps://hypery.ai) and closes. - New tab / redirect: the user is sent to
returnUrl?subscribe_session=<id>&state=<state>&subscribe_status=completed|cancelled.
Always confirm the outcome with your token (don't trust the message alone):
Endpoint: GET /api/marketplace/subscribe-sessions/{sessionId}/result (scope read)
{
"success": true,
"status": "completed",
"state": "c0ffee-8f3a1",
"team": { "id": "66a…", "name": "Acme" },
"subscription": { "id": "670…", "status": "active", "interval": "year", "nextGrantAt": "…" }
}status is pending, completed, cancelled, or expired. Only the app that created
the session can read it.
Show available plans
Endpoint: GET /api/marketplace/plans (scope read)
With an OAuth token, this returns the plans of the app the token belongs to. Session or API-key callers pass ?appId={appId}.
{
"plans": [
{
"id": "66f…",
"appId": "66e…",
"name": "Pro",
"priceCents": 1000,
"currency": "usd",
"interval": "month",
"prices": [{ "interval": "month", "priceCents": 1000 }, { "interval": "year", "priceCents": 10000 }],
"grants": [{ "type": "hypery", "amountUsd": 10, "expiresAfterDays": 31, "issueOn": "each_payment" }]
}
]
}Subscribe directly (server-to-server)
Prefer the hosted session above. This endpoint subscribes the caller's resolved billing team without asking which team, and is kept for existing integrations.
Endpoint: POST /api/marketplace/subscribe (scope billing:charge)
curl -X POST https://hypery.ai/api/marketplace/subscribe \
-H "Authorization: Bearer <user OAuth token>" \
-H "Content-Type: application/json" \
-d '{ "planId": "66f…", "interval": "year", "idempotencyKey": "sub-7f3c9a1e" }'Responses:
The grants go to the caller's billing team: their personal team, or the
organization selected with x-hypery-active-organization-id.
Status and remaining grants
Endpoint: GET /api/marketplace/subscriptions?appId={appId}[&teamId={teamId}] (scope read)
Returns the subscriptions of every team the user belongs to (filter with teamId).
{
"subscriptions": [
{
"id": "670…",
"appId": "66e…",
"team": { "id": "66a…", "name": "Acme", "slug": "acme" },
"plan": { "id": "66f…", "name": "Pro", "priceCents": 1000, "interval": "month", "grants": [] },
"status": "active",
"cancelAtPeriodEnd": false,
"interval": "year",
"pendingInterval": null,
"currentPeriodEnd": "2027-09-15T12:00:00.000Z",
"nextGrantAt": "2026-10-15T12:00:00.000Z",
"grants": [
{ "type": "hypery", "amountUsd": 10, "remainingUsd": 6.42, "expiresAt": "2026-10-15T12:00:00.000Z" }
]
}
]
}Gating your app's own features
Hypery doesn't store your app's features; map them from the plan in your app. To decide
what a team can use, call the endpoint above with teamId (the team the user is working
in, from list_memberships) and treat the team as subscribed when a subscription has:
statusofactiveortrialing(optionally allow a short grace period forpast_due), andplan.idof the plan that unlocks the feature (cancelAtPeriodEnd: truestill has access untilcurrentPeriodEnd).
Check it on your server before doing gated work, and cache it briefly per team.
Cancel and resume
POST /api/marketplace/subscriptions/{id}/cancel: stops renewal at the end of the current period. Grants already issued stay usable until they expire.POST /api/marketplace/subscriptions/{id}/resume: undoes a pending cancellation.
Switch between monthly and annual
Endpoint: POST /api/marketplace/subscriptions/{id}/interval with { "interval": "year" | "month" }
- Monthly → annual happens immediately. The unused part of the current
month is credited, the year is charged now, and a new yearly period starts. The
current month's grants aren't issued twice. A declined card returns
402 PAYMENT_DECLINEDand nothing changes. - Annual → monthly happens at the end of the paid year (
effective: "period_end",pendingInterval: "month"). Monthly grants continue until then. Calling it again withyearkeeps annual billing. - Switching to annual is refused on an archived plan (
409 PLAN_ARCHIVED).
All three require billing:charge. The original subscriber or any owner/admin of the
subscribing team may call them, and an OAuth token can only manage subscriptions for
its own app.
Lifecycle
App Store In-App Purchase
An app shipped through the App Store must sell subscriptions and credits with In-App Purchase, not with Hypery's (Stripe) checkout. Hypery applies those purchases for you: an App Store subscription becomes a regular subscription to one of your plans and issues its grants on every renewal, and a consumable credit pack adds credits to the buyer's team. Apple bills, renews, cancels and refunds; Hypery mirrors it.
1. Set it up
In App Store Connect, create your products (an auto-renewable subscription group for
your plans, consumables for credit packs) and set App Store Server Notifications
(Version 2) to https://hypery.ai/api/marketplace/apple/notifications for both
Production and Sandbox. Then tell Hypery what each product is:
PUT /api/apps/{appId}/apple
Authorization: Bearer <owner's API key or session>
{
"bundleIds": ["com.example.app"],
"commissionBps": 1500,
"products": [
{ "productId": "pro.monthly", "kind": "subscription", "planId": "<planId>", "interval": "month" },
{ "productId": "credits.849", "kind": "credits", "credits": 849 }
],
"sandboxUserIds": ["<Hypery user id of your App Review demo account>"]
}GET /api/apps/{appId}/apple returns the setup plus pricing guidance: for each plan
price, the App Store price that leaves you at least what the web price does after
Apple's commission, and how many credits each common pack price can fund. Credit packs
are funded by your app's owner team, like grants.
2. Sell from the app
GET /api/marketplace/apple/config(the user's OAuth token) returns your product ids and the user'sappAccountToken.- Buy with StoreKit 2, passing that token:
product.purchase(options: [.appAccountToken(token)]). - Send the transaction's JWS to Hypery, then finish the transaction only on 200:
POST /api/marketplace/apple/transactions
Authorization: Bearer <user's OAuth token>
x-hypery-active-organization-id: <team the purchase is for>
{ "signedTransaction": "<Transaction.jwsRepresentation>" }Hypery verifies Apple's signature, checks the purchase belongs to this user and app, and
applies it once (replays are no-ops). Send every transaction you see — purchases,
Restore Purchases, and Transaction.updates — the same way. 503 means try again
later (keep the transaction unfinished; StoreKit re-delivers it); any 4xx will never
succeed, so finish it.
A subscription bought in the App Store has "provider": "apple" in
GET /api/marketplace/subscriptions. It can only be changed or canceled in the App
Store (show AppStore.showManageSubscriptions); Hypery's cancel, resume, plan and
interval endpoints answer 409 MANAGED_BY_APP_STORE.
3. What notifications do
A notification for a subscription your app hasn't reported yet is answered 500 so
Apple retries it after the app reports the purchase (which says which team it is for).
Deleting an account
Deleting a Hypery account can't cancel its App Store subscriptions — only the user can,
in Settings → Apple Account → Subscriptions. Hypery stops issuing their grants, and
DELETE /api/user/me answers 200 { appStoreSubscriptions: n, message } (instead of
204) so your app can tell the user.
Paying for usage in your App Store build
Usage in an App Store build must also be paid without charging a card the user saved
elsewhere: send x-hypery-payment-source: credits on every AI request from that build.
See Paying from credits only.