Subscriptions
Recurring billing — create a plan, subscribe a customer, and track each cycle's deduction.
Bill a customer on a schedule. The model is plan → subscription → recurring deductions: define a
plan once, subscribe any number of customers to it, and each billing cycle produces a deduction
that fires the same payment.succeeded webhook as a one-off charge.
Creating plans and subscriptions needs the subscriptions:write scope; reads need
subscriptions:read.
1. Create a plan
A plan is the reusable template: the per-cycle amount, the billing interval, and how many cycles to bill in total.
const plan = await ap.subscriptions.createPlan({
merchantPlanNo: "pro-monthly", // your unique plan reference
name: "Pro (monthly)",
amount: { amount: "9.99", currency: "USDT" }, // decimal string, ≤ 6 dp
interval: "MONTH", // DAY | WEEK | MONTH | YEAR
intervalCount: 1, // e.g. MONTH × 3 = quarterly
totalCycles: 12, // total charges before the plan completes
// trialDays: 14, // optional free trial: no charge for N days after the customer authorizes
});
console.log(plan.planNo);plan = ap.subscriptions.create_plan(
merchant_plan_no="pro-monthly", # your unique plan reference
name="Pro (monthly)",
amount={"amount": "9.99", "currency": "USDT"}, # decimal string, ≤ 6 dp
interval="MONTH", # DAY | WEEK | MONTH | YEAR
interval_count=1, # e.g. MONTH × 3 = quarterly
total_cycles=12, # total charges before the plan completes
# trial_days=14, # optional free trial: no charge for N days after the customer authorizes
)
print(plan["planNo"])plan, err := ap.Subscriptions.CreatePlan(ctx, absolutepay.PlanParams{
MerchantPlanNo: "pro-monthly", // your unique plan reference
Name: "Pro (monthly)",
Amount: absolutepay.Money{Amount: "9.99", Currency: "USDT"}, // decimal string, ≤ 6 dp
Interval: "MONTH", // DAY | WEEK | MONTH | YEAR
IntervalCount: 1, // e.g. MONTH × 3 = quarterly
TotalCycles: 12, // total charges before the plan completes
// TrialDays: 14, // optional free trial: no charge for N days after the customer authorizes
})
if err != nil {
log.Fatal(err)
}
fmt.Println(plan["planNo"])BODY='{"merchantPlanNo":"pro-monthly","name":"Pro (monthly)","amount":{"amount":"9.99","currency":"USDT"},"interval":"MONTH","intervalCount":1,"totalCycles":12}'
curl https://api.absolutepay.io/v1/subscription-plans \
-H "Authorization: Bearer $APP_TOKEN" -H "Content-Type: application/json" \
$(sign POST /v1/subscription-plans "$BODY") -d "$BODY"{ "planNo": "plan_01JX…", "merchantPlanNo": "pro-monthly", "name": "Pro (monthly)", "amount": { "amount": "9.99", "currency": "USDT" }, "interval": "MONTH", "intervalCount": 1, "totalCycles": 12 }Keep the returned planNo — it's what you pass when subscribing a customer. merchantPlanNo must be
unique per workspace (a duplicate is a 409). List existing plans with ap.subscriptions.listPlans()
/ GET /v1/subscription-plans.
Free trial. Set trialDays on the plan (0–365) to give every subscriber a trial: the customer
authorizes but isn't charged until the trial ends, then billing begins automatically. A subscription
in its trial reports the TRIALING status. Omit trialDays (or 0) to bill immediately on
authorization.
2. Subscribe a customer
const sub = await ap.subscriptions.create({
merchantSubNo: "sub-1001", // your reference + idempotency key
planNo: plan.planNo,
// callbackUrl: "https://…", // optional per-subscription notification URL
});
console.log(sub.subscribeUrl); // send the customer here to authorizesub = ap.subscriptions.create(
merchant_sub_no="sub-1001", # your reference + idempotency key
plan_no=plan["planNo"],
# callback_url="https://…", # optional per-subscription notification URL
)
print(sub["subscribeUrl"]) # send the customer here to authorizesub, err := ap.Subscriptions.Create(ctx, absolutepay.SubscribeParams{
MerchantSubNo: "sub-1001", // your reference + idempotency key
PlanNo: plan["planNo"].(string),
// CallbackURL: "https://…", // optional per-subscription notification URL
})
if err != nil {
log.Fatal(err)
}
fmt.Println(sub["subscribeUrl"]) // send the customer here to authorizeBODY='{"merchantSubNo":"sub-1001","planNo":"plan_01JX…"}'
curl https://api.absolutepay.io/v1/subscriptions \
-H "Authorization: Bearer $APP_TOKEN" -H "Content-Type: application/json" \
$(sign POST /v1/subscriptions "$BODY") -d "$BODY"{ "merchantSubNo": "sub-1001", "subId": "…", "status": "PENDING", "planNo": "plan_01JX…", "subscribeUrl": "https://…" }The subscription starts PENDING. Send the customer to the hosted subscribeUrl to authorize; once
they do, it goes ACTIVE and bills automatically each cycle (or TRIALING first, if the plan has a
trial). merchantSubNo is your idempotency key — re-POSTing the same one won't create a duplicate
subscription.
Telling integrations apart
A subscription created with an API key carries an appId — the id of the key that created it — so a
workspace running several integrations can attribute each subscription to the right one. Subscriptions
created from the dashboard have no appId.
3. Track the cycles
Each successful cycle fires a payment.succeeded webhook — handle it exactly like a
one-off payment (idempotent on event.id). To reconcile or backfill, pull the per-cycle history:
const history = await ap.subscriptions.deductions("sub-1001");
// each deduction: paymentOrderNo, amount, status (SUCCESS | FAILED | PENDING | BLOCKED), tshistory = ap.subscriptions.deductions("sub-1001")
# each deduction: paymentOrderNo, amount, status (SUCCESS | FAILED | PENDING | BLOCKED), tshistory, err := ap.Subscriptions.Deductions(ctx, "sub-1001")
if err != nil {
log.Fatal(err)
}
fmt.Println(history) // paymentOrderNo, amount, status (SUCCESS | FAILED | PENDING | BLOCKED), tscurl "https://api.absolutepay.io/v1/subscriptions/sub-1001/deductions" \
-H "Authorization: Bearer $APP_TOKEN" $(sign GET /v1/subscriptions/sub-1001/deductions "")List all subscriptions with ap.subscriptions.list() / GET /v1/subscriptions — keyset-paginated
(limit, before, optional status filter; see Pagination).
4. Cancel
await ap.subscriptions.cancel("sub-1001"); // stop all future billingap.subscriptions.cancel("sub-1001") # stop all future billing_, err := ap.Subscriptions.Cancel(ctx, "sub-1001") // stop all future billing
if err != nil {
log.Fatal(err)
}curl -X POST https://api.absolutepay.io/v1/subscriptions/sub-1001/cancel \
-H "Authorization: Bearer $APP_TOKEN" $(sign POST /v1/subscriptions/sub-1001/cancel "")Cancelling stops future cycles only — settled deductions are crypto payments and can't be clawed back. To return one, issue a refund.
Subscription statuses
PENDING — created, waiting for the customer to authorize on subscribeUrl. TRIALING — authorized
and in its free trial; billing hasn't started yet. ACTIVE — billing each cycle. PAST_DUE — a
cycle's charge failed. COMPLETED — all totalCycles billed. CANCELLED / BLOCKED — no further billing.
Next
- Webhooks — receive each cycle's
payment.succeededreliably. - Fees — how platform fees are disclosed. Subscription cycle fees are settled per cycle and aren't priced by the preview endpoint.
- Refunds — return a settled cycle's charge.
- API reference — full parameter and response tables for every subscription endpoint.