powered by

Offer codes, free trials and entitlements

Run referral programmes and promo codes, give new users a free trial before they subscribe, and check what a subscriber can access with one entitlement call.

Last updated

Three related but separate things sit behind AnythinkPay's /me calls: offers (referral programmes and promo codes), the app free trial (access before any subscription) and entitlements (the one check your app makes to gate a feature). This page covers all three, plus user balances if your project uses account credit. For the wider model, see How payments and subscriptions work.

Offers and codes

An offer is a referral programme or a promotion: a set of codes, an optional eligibility rule and a reward. AnythinkPay creates a default referral offer the first time someone asks for their personal code, so a project with no offers configured still works.

In the Anythink dashboard

Offers live under Growth. Project administrators manage them:

  • Referrals lists referral programmes. New programme creates one, where you set what the new joiner gets and what the referrer gets when a code is redeemed. Each programme has Settings, Members (each member's own code) and Redemptions views.
  • Promo codes lists one-off codes such as LAUNCH50. New promo code takes a Code, an optional Internal name, a Reward, optional Valid from and Valid until dates, a Total cap, a Per-user cap and a Status. The Codes and Redemptions tabs show each code and who used it.
  • Balances shows the credit and points users have earned, as Holders and Activity.

A promo code can be limited to certain users with Restricted: users must hold an active subscription on specific plans, hold a minimum plan tier, or have redeemed a specific code before. Set the tier on each plan with Tier rank. See Create plans and manage subscriptions.

The Reward can be Extend free trial (a number of days), Points or Account credit (an amount and a currency). A redeemer receives the reward the moment they redeem the code.

With the CLI

bash
anythink fetch /integrations/anythinkpay/offers
anythink fetch /integrations/anythinkpay/offers/{id}/codes
anythink fetch /integrations/anythinkpay/offers/redemptions

To create an offer, post it with fetch:

bash
anythink fetch /integrations/anythinkpay/offers --method POST --body \
  '{"kind":"referral","name":"Referral programme","status":"active","redeemer_reward_json":"{\"type\":\"trial_extension\",\"days\":7}"}'

kind is referral or trial_extension. status is active, paused or expired. redeemer_reward_json is required. It's what the person redeeming a code gets, written as a JSON string. Use trial_extension with days, points with amount, or credit with amount and currency. Optional fields are description, valid_from, valid_until, total_redemption_cap, per_user_redemption_cap (default 1) and eligibility_json.

Add a code to an offer with POST .../offers/{id}/codes and a body such as {"slug":"LAUNCH50"}.

With an AI assistant (MCP)

text
List redemptions for the referral offer on this project.

A subscriber's own codes and referrals

Your app calls these as the signed-in user. They need the subscriber-level anythink_subscription_plans:read permission rather than the administrator-level anythink_offers:read, so a mobile app's own token can call them without administrator rights:

http
GET  /org/{project_id}/integrations/anythinkpay/offers/me/code
GET  /org/{project_id}/integrations/anythinkpay/offers/me/codes
POST /org/{project_id}/integrations/anythinkpay/offers/me/redeem/{slug}
GET  /org/{project_id}/integrations/anythinkpay/offers/me/referrals
  • me/code returns this user's personal code for the default referral programme, creating it the first time.
  • me/codes returns their code for every active referral programme.
  • me/referrals returns their personal_code_slug, redemption_count, rewards_applied, rewards_pending and a recent list. It includes tier_progress when the programme gives tiered rewards.
  • me/redeem/{slug} redeems a code as the user. Add ?subscription_id= when the redemption should apply to a specific subscription.

A rejected code still returns 200, so check success:

json
{
  "success": false,
  "rejection_reason": "Already redeemed",
  "redemption_id": null,
  "redeemer_reward_summary": null,
  "referrer_reward_queued": false
}

rejection_reason says why: the code doesn't exist, the offer isn't active yet, has expired or has reached its cap, the user has already redeemed it, it's their own code, or they don't meet the eligibility rule. Eligibility is worked out for you from the redeeming user's active subscriptions.

Free trials

There are two separate kinds of trial:

  • A plan's own trial (Trial period (days) on the plan) delays a subscriber's first charge after they check out. See Create plans and manage subscriptions.
  • The app free trial gives one window of free access to a user who has never subscribed. It doesn't depend on a plan or a payment provider.

In the Anythink dashboard

  1. Open Growth and choose the Engagement tab.
  2. Tick Offer a free trial to users who haven't subscribed.
  3. Set Trial length (days), from 1 to 365, and select Save.

The trial is off until you tick the box. It starts the first time that user calls the entitlement endpoint, runs once per user and never restarts. A promo code or referral reward of Extend free trial adds days to it.

With the CLI

The trial switch is a project setting you change in the dashboard. Read its result with the entitlement call below.

Entitlements

One endpoint answers "can this user use the paid feature right now?" across Stripe, Apple and the app free trial:

http
GET /org/{project_id}/integrations/anythinkpay/subscriptions/me/entitlement

For a Stripe subscriber in the middle of their plan's trial:

json
{
  "has_access": true,
  "subscription_id": "4c2f9a1e-0000-0000-0000-000000000000",
  "provider": "stripe",
  "product_id": "Pro monthly",
  "status": "trialing",
  "expires_at": "2026-10-11T17:49:06Z",
  "auto_cancel_at": null,
  "cancelled_at": null,
  "is_trial": false,
  "trial_ends_at": null,
  "trial_days_remaining": null,
  "trial_length_days": 7,
  "trial_base_days": 0,
  "trial_bonus_days": 0,
  "pending_product_id": null,
  "pending_at": null
}

For a user on the app free trial, with a 7-day trial and a 14-day promo bonus, the subscription fields are empty and the trial fields are filled:

json
{
  "has_access": true,
  "subscription_id": null,
  "provider": null,
  "product_id": null,
  "status": "trialing",
  "expires_at": "2026-10-25T09:00:00Z",
  "auto_cancel_at": null,
  "cancelled_at": null,
  "is_trial": true,
  "trial_ends_at": "2026-10-25T09:00:00Z",
  "trial_days_remaining": 21,
  "trial_length_days": 21,
  "trial_base_days": 7,
  "trial_bonus_days": 14,
  "pending_product_id": null,
  "pending_at": null
}
Field Meaning
has_access The only field you need to gate a feature
provider, product_id, status The subscription behind the answer. product_id is the subscription's name, which matches a plan's name.
expires_at When the current period, or the free trial, ends
auto_cancel_at, cancelled_at Set when the subscriber has turned off renewal
is_trial, trial_ends_at, trial_days_remaining Set while the user is on the app free trial. A subscriber on a plan's own trial has is_trial false, and the plan's trial shows as status trialing.
trial_length_days, trial_base_days, trial_bonus_days The app free trial's total length, split into the project's setting and days added by codes. For a subscriber, trial_length_days is the project's setting.
pending_product_id, pending_at An Apple plan change that takes effect at the next renewal
  • A user who has never subscribed is resolved from the app free trial. With it off, they get has_access false and status null. While it runs, has_access and is_trial are true and status is trialing. After it ends, has_access is false and status is trial_expired, which is your cue to show a paywall.
  • If a user has several subscriptions, the call returns the strongest one: one that grants access first, then the one that runs longest.
  • pending_product_id and pending_at let you show "switching to X on your next renewal".
  • A user who subscribes stops using the trial and is read from their subscription instead.

The call needs anythink_subscription_plans:read, so a subscriber's own token works and your client needs no administrator key. Payments recipes shows how to grant the permission and gate a feature with it. Use the signed-in user's token, because an API key has no subscriber behind it.

User balances

If your project uses account credit or points, a subscriber reads their own balance the same way, with the subscriber-level permission:

http
GET /org/{project_id}/integrations/anythinkpay/balances/me
json
[
  { "unit_type": "points", "currency": null, "total": 100 },
  { "unit_type": "credit", "currency": "gbp", "total": 500 }
]

Points are whole numbers. Credit is in minor units, so 500 is £5.00.

Administrators can also read any user's balance and history, and adjust it by hand. Those calls need anythink_balances:read and anythink_balances:update:

http
GET  .../balances/users/{user_id}
GET  .../balances/users/{user_id}/history
POST .../balances/users/{user_id}/adjust
{ "unit_type": "credit", "currency": "gbp", "delta": 500, "reason": "Goodwill credit" }

Note: AnythinkPay stores balances and lets you adjust them, but it doesn't spend a balance against a charge. If you want "use credit first, then the card", that logic lives in your own workflow or backend, using the balance calls above.

Permissions

To You need
Read your own code, codes and referrals, and redeem a code anythink_subscription_plans:read
Read your own entitlement anythink_subscription_plans:read
Read your own balance anythink_subscription_plans:read
List, create, update or delete offers and codes, and see all redemptions anythink_offers permissions
Read or adjust another user's balance anythink_balances:read, anythink_balances:update

Next steps