For the complete documentation index, see llms.txt. This page is also available as Markdown.

Subscriptions

Subscriptions let you bill a customer automatically and recurringly in crypto. You define a Plan (price, currency, billing interval), subscribe a customer to it, and IvoryPay takes care of pulling payment from the customer's wallet on every billing cycle until the subscription is cancelled.

Billing works on a one-time on-chain approval model: the customer approves a token allowance once (via an IvoryPay-hosted checkout page), and IvoryPay charges against that allowance on each due date — no further action from the customer is required for renewals.

Base path: /v1/subscriptions Authentication: Secret key (Authorization: sk_...)


How the flow works

  1. Create a plan with POST /v1/subscriptions/plans.

  2. Create a subscription for the customer with POST /v1/subscriptions/create. IvoryPay returns an approvalUrl.

  3. Send the customer to the hosted checkout page at approvalUrl. The customer connects their wallet and approves the token allowance there.

  4. After approval, IvoryPay creates the underlying subscription record and automatically triggers the first charge attempt.

  5. If the first charge succeeds, the subscription becomes ACTIVE. If it fails, IvoryPay retries for a short activation window and marks the subscription NOT_APPROVED if no charge lands.

  6. Once the subscription is ACTIVE, IvoryPay auto-charges the customer on every billing cycle.

  7. Failed renewal charges retry according to the plan's retry policy and fire an invoice.payment_failed webhook.

  8. You can cancel at any time. Future charges stop after cancellation.

Only step 1 and 2 are things you call directly. Step 3 happens on a page IvoryPay hosts for you (checkout.ivorypay.io) — you never see or handle the customer's wallet or on-chain approval yourself. See Hosted Checkout below for what happens there.


Endpoints

Method
Path
Description

POST

/v1/subscriptions/plans

Create a billing plan

GET

/v1/subscriptions/plans

List plans (paginated)

GET

/v1/subscriptions/plans/:planCode

Get a single plan

PATCH

/v1/subscriptions/plans/:planCode

Update a plan

POST

/v1/subscriptions/create

Start a subscription for a customer

PATCH

/v1/subscriptions/:subCode/cancel

Cancel a subscription

GET

/v1/subscriptions/subscribers

List subscribers (customer + plan pairs)

GET

/v1/subscriptions

List subscriptions

GET

/v1/subscriptions/:id

Get a single subscription by ID


POST /v1/subscriptions/plans

Creates a billing plan. planCode is generated by IvoryPay and returned in the response — you'll use it to subscribe customers.

Body Parameters

Parameter
Type
Required
Description

planName

string

Yes

Display name for the plan

description

string

No

Free-text description

amountType

"CRYPTO"

Yes

Only CRYPTO is accepted right now.

currency

string

Yes

The fiat currency the plan is priced in, e.g. USD (see note below).

amount

number

Yes

Price per billing cycle, in currency. Must be >= 0.

interval

string

Yes

Billing frequency. One of: HOURLY, DAILY, WEEKLY, MONTHLY, QUARTERLY, BIANNUALLY, YEARLY

Pricing note: amount/currency are always priced in a fiat reference currency (e.g. 9.99 USD/month). The customer picks the crypto token and network they want to pay with when they complete checkout, and IvoryPay converts amount into that token at the prevailing rate on every charge — so the crypto amount pulled can vary slightly cycle to cycle with the exchange rate, while the fiat price stays fixed.

Request

Response


GET /v1/subscriptions/plans

Lists your plans, enriched with subscriber counts and revenue.

Query Parameters

Parameter
Type
Required
Description

page

number

No

Page number. Defaults to 1.

limit

number

No

Results per page. Defaults to 10, max 100.

q

string

No

Case-insensitive search against planName or planCode.

isActive

boolean

No

Filter by active/inactive plans.

Request

Response

Response Fields

Field
Type
Description

records[].numberOfSubscriptions

number

Total subscriptions ever created on this plan (any status)

records[].totalRevenue

number

Sum of lifetimeValue across all subscriptions on this plan

count

number

Total plans matching the query, across all pages


GET /v1/subscriptions/plans/:planCode

Returns a single plan, including basic business info (shown to customers on the checkout page).

Request

Response

Returns 404 Plan not found if the plan doesn't exist, belongs to another business, or has been deleted.


PATCH /v1/subscriptions/plans/:planCode

Updates a plan. All fields are optional — only what you send is changed.

Body Parameters

Parameter
Type
Required
Description

planName

string

No

New display name

description

string

No

New description

amount

number

No

New price

interval

string

No

New billing interval

isActive

boolean

No

Set to false to stop new subscribers from subscribing to this plan

updateExistingSubscriptions

boolean

No

If true, cascades a new amount/interval to every active subscriber (see note below)

Existing subscribers: By default, changing amount or interval only affects new subscriptions — subscribers already on the plan keep paying their original price/schedule. Pass updateExistingSubscriptions: true to roll the change out to every active subscriber. The new amount/interval takes effect on each subscriber's next billing cycle, not immediately — there's no forced re-approval or interruption to the current cycle.

Request

Response


POST /v1/subscriptions/create

Starts a subscription for a customer. This doesn't charge anyone yet — it creates (or reuses) a pending checkout and gives you a URL to send the customer to, where they connect their wallet and approve the on-chain allowance that IvoryPay will bill against.

Body Parameters

Parameter
Type
Required
Description

email

string

Yes

Customer's email. Used to find-or-create the customer record.

planCode

string

Yes

Code of the plan to subscribe them to. Must be an active plan.

firstName

string

No

Customer's first name

lastName

string

No

Customer's last name

Request

Response

Redirect the customer to approvalUrl (or embed it) to complete setup. Calling this again with the same email + planCode while a checkout is still pending returns the same approvalUrl rather than creating a duplicate.

Errors

Status
Message
Cause

400

Plan not found

planCode doesn't exist, or isn't active


PATCH /v1/subscriptions/:subCode/cancel

Cancels a subscription. Only subscriptions in ACTIVE can be cancelled. No further charges are attempted once cancelled — this cannot be undone via the API.

Request

Response

Errors

Status
Message
Cause

400

Subscription not found

subCode doesn't exist for this business

400

Subscription is currently not active

Subscription isn't ACTIVE (e.g. CANCELLED, PENDING_APPROVAL, EXPIRED, NOT_APPROVED)


GET /v1/subscriptions/subscribers

Lists your subscribers — one row per subscription, paired with the customer behind it. Useful for a "who's subscribed" view rather than a transaction-style list.

Query Parameters

Parameter
Type
Required
Description

page

number

No

Page number. Defaults to 1.

limit

number

No

Results per page. Defaults to 10, max 100.

planId

string

No

Filter by plan UUID.

status

string

No

q

string

No

Search by customer email (exact) if it contains @, otherwise by customer name.

Request

Response

startedAt — when the subscription first went ACTIVE — not when the checkout was initiated.


GET /v1/subscriptions

Lists raw subscription records for your business (all statuses by default).

Query Parameters

Parameter
Type
Required
Description

page

number

No

Page number. Defaults to 1.

limit

number

No

Results per page. Defaults to 10, max 100.

planId

string

No

Filter by plan UUID.

status

string

No

q

string

No

Search by customer email (exact) if it contains @, otherwise by subCode (partial, case-insensitive).

Request

Response


GET /v1/subscriptions/:id

Returns a single subscription by its UUID (id, not subCode).

Request

Response

Returns 404 Subscription not found if id doesn't exist or belongs to another business.


Subscription Status Values

Status
Meaning

PENDING_APPROVAL

Checkout submitted, but the first on-chain charge hasn't succeeded yet.

ACTIVE

Approved and billing normally — will be auto-charged on each interval.

NOT_APPROVED

The customer never completed (or abandoned) approval within the activation window.

CANCELLED

Cancelled — no further charges will be attempted.

EXPIRED

Subscription lapsed.


Webhooks

Two events fire during the billing lifecycle, on top of the standard payment webhooks. Register for them the same way — see Webhooks.

Event
Fires when

invoice.create

Every time IvoryPay attempts to charge a subscriber for a billing cycle (including the first activation charge).

invoice.payment_failed

A charge attempt for a subscriber failed.

Payload

failureReason is only populated on invoice.payment_failed. attemptNumber starts at 1 and increments on each retry for the same billing cycle — use it together with reference to de-duplicate retries in your handler.

A failed renewal charge is retried automatically on a delay based on the plan's interval; a failed first charge (subscription still PENDING_APPROVAL) is retried on a short fixed interval until the activation window elapses, after which the subscription is marked NOT_APPROVED and stops retrying.


Hosted Checkout Page

approvalUrl points to checkout.ivorypay.io, an IvoryPay-hosted page that handles wallet connection and the on-chain allowance approval for you. You don't need to build this yourself.

Error Responses

Status
Message
Cause

401

Unauthorized

Missing or invalid API key

400

Plan not found

planCode doesn't exist, isn't active, or doesn't belong to your business

400

Subscription not found

subCode doesn't exist, or doesn't match the required status for the action

404

Subscription not found

id doesn't exist, or belongs to another business (on GET /:id)

400

Subscription is currently not active

Attempted to cancel a subscription that isn't ACTIVE


Code Examples

Node.js — Create a plan and subscribe a customer

Node.js — Cancel a subscription

cURL — Create a plan

cURL — Subscribe a customer


Notes

  • All endpoints are scoped to the authenticated business and environment — use your live key for live data and your test key for test data.

  • count in list responses reflects the total matching records, not just the current page.

  • A subscription only starts costing the customer anything once they complete approval on the hosted checkout page — creating a subscription via the API is free of side effects on-chain.

  • A charge won't fire earlier than the subscription's nextChargeDate.

Last updated