Subscriptions let you charge customers any supported ERC-20 token on a fixed interval, minute through yearly, with optional free trials. Create plans in the dashboard or API, share hosted signup links, and let Videpay handle wallet approvals, recurring charges, past_due retries, and customer self-service portals. Recurring revenue, minus the 'did their card expire?' panic spiral.
Need machine-readable request/response bodies for your AI or codebase? Download this page as plain Markdown.
You create a $29/month USDC plan with a 7-day trial, generate a public signup link from the dashboard, and email it to prospects. Customers approve the billing token once on the hosted page. After trial, Videpay charges every 30 days and sends subscription.charged. If a charge fails, status becomes past_due and Videpay retries up to three times before cancellation. Three strikes, then the subscription logs off.
Everything in the dashboard maps to the API
Open Dashboard → Subscriptions. The Plans tab covers plan creation, fee quotes, deactivation, and signup links. The Subscribers tab covers listing, filtering, manual subscription creation, cancel, charge, and portal links. Customer portal and signup pages are hosted at signupUrl and portalUrl from link responses.
Before creating a plan, preview the protocol fee deducted from each charge. Fees come from the on-chain TokenRegistry Subscription fee bucket.
| Field | Type | Required | Description |
|---|---|---|---|
chain | string | No | Chain scope for the request (e.g. base, polygon). Defaults to your dashboard chain selection. Sandbox resolves to testnets automatically. |
asset | string | Yes | Asset symbol to quote (any supported ERC-20 on the chain). |
amount | decimal string | Yes | Plan price to quote before creating the plan. |
/subscriptions/plans/quote-feePreview gross, fee, and net amounts for a plan price.
01x-api-key: sk_AbCdEfGhIj_XyZ1234567890abcdefghijklmnopqrstuvwxyz01234502Content-Type: application/json01{02 "asset": "USDC",03 "chain": "base",04 "amount": "29"05}| Field | Type | Required | Description |
|---|---|---|---|
chain | string | No | Chain scope for the request (e.g. base, polygon). Defaults to your dashboard chain selection. Sandbox resolves to testnets automatically. |
name | string | Yes | Plan display name shown on hosted signup pages (max 120 characters). |
description | string | No | Optional plan description (max 500 characters). |
asset | string | Yes | Billing asset symbol. Any ERC-20 token supported on the target chain (e.g. USDC, USDT, EURC). Native assets are not supported. |
amount | decimal string | Yes | Recurring charge amount per interval in human units (e.g. "29" for 29 tokens). |
interval | enum | Yes | Billing cadence: minute, hourly, weekly, monthly, or yearly. Intervals use fixed seconds (monthly = 30 days, yearly = 365 days), not calendar-aware. |
trialSeconds | integer | No | Free trial length in seconds. Takes precedence over trialDays when both are sent. |
trialDays | integer | No | Free trial length in days (converted to seconds server-side). Ignored when trialSeconds is provided. |
planId | uuid | No | Optional custom plan UUID. If omitted, Videpay generates one. Must be unique per merchant chain profile. |
authorization | object | No | Dashboard-only step-up auth when calling from the browser with a JWT session. API key integrations omit this. Supports pin, otpChallengeId, and twoFactorCode. On sandbox, any emailed OTP code is `000000`. |
authorization.pin | string | No | Transaction PIN when enabled for your business. |
authorization.otpChallengeId | uuid | No | OTP challenge ID from a prior dashboard auth flow. On sandbox, verify the emailed code with `000000`. |
authorization.twoFactorCode | string | No | TOTP code when 2FA is enabled. |
/subscriptions/plansCreate a recurring billing plan. Emits subscription.plan.created.
01x-api-key: sk_AbCdEfGhIj_XyZ1234567890abcdefghijklmnopqrstuvwxyz01234502Content-Type: application/json01{02 "name": "Pro Monthly",03 "description": "Full platform access billed monthly in USDC.",04 "asset": "USDC",05 "chain": "base",06 "amount": "29",07 "interval": "monthly",08 "trialDays": 709}| Field | Type | Required | Description |
|---|---|---|---|
includeInactive | boolean | No | Query param on list. Default false: only active plans. |
offset / limit | integer | No | Pagination. limit is 1–100, default 20. |
/subscriptions/plans?includeInactive=true&limit=20List plans for the business and chain. Set includeInactive=true to include deactivated plans.
01x-api-key: sk_AbCdEfGhIj_XyZ1234567890abcdefghijklmnopqrstuvwxyz01234502Content-Type: application/json/subscriptions/plans/pln_7f3a9b2c-4d1e-4a8f-9c0b-1e2d3f4a5b6cRetrieve a single plan by planId.
01x-api-key: sk_AbCdEfGhIj_XyZ1234567890abcdefghijklmnopqrstuvwxyz01234502Content-Type: application/json/subscriptions/plans/pln_7f3a9b2c-4d1e-4a8f-9c0b-1e2d3f4a5b6c/deactivateDeactivate a plan. Existing subscriptions continue; new signups are blocked. Emits subscription.plan.deactivated.
01x-api-key: sk_AbCdEfGhIj_XyZ1234567890abcdefghijklmnopqrstuvwxyz01234502Content-Type: application/json01{}Generate hosted signup URLs customers open in a browser. Links use pub-* access tokens. Share signupUrl from the response: customers never need your API key.
| Field | Type | Required | Description |
|---|---|---|---|
chain | string | No | Chain scope for the request (e.g. base, polygon). Defaults to your dashboard chain selection. Sandbox resolves to testnets automatically. |
customerId | uuid | No | Bind the signup link to an existing customer record. When set, reference and metadata can personalize the invite. |
reference | string | No | Your invite or order reference (max 100). Must be unique per business when used. |
metadata | object | No | Arbitrary JSON stored on the signup link and copied to the subscription when created. |
expiresInDays | integer | No | Link expiry in days (1–3650). Omit for a permanent link. Expired links return an error on public endpoints. |
/subscriptions/plans/pln_7f3a9b2c-4d1e-4a8f-9c0b-1e2d3f4a5b6c/public-linkCreate a shared signup link for any customer, or bind to customerId for personalized invites.
01x-api-key: sk_AbCdEfGhIj_XyZ1234567890abcdefghijklmnopqrstuvwxyz01234502Content-Type: application/json01{02 "customerId": ":cus_3b7e9f1a-2c4d-5e6f-7a8b-9c0d1e2f3a4b",03 "reference": "invite-1042",04 "metadata": {05 "campaign": "summer-2026"06 },07 "expiresInDays": 3008}/subscriptions/public/plans/pub-a1b2c3d4e5f6789012345678Public: load plan details and subscribable availability (no auth).
/subscriptions/public/plans/pub-a1b2c3d4e5f6789012345678/preparePublic: check token balance/allowance and return typed data for wallet approval.
01Content-Type: application/json01{02 "customerAddress": "0x742d35Cc6634C0532925a3b844Bc454e4438f44e"03}/subscriptions/public/plans/pub-a1b2c3d4e5f6789012345678/subscribePublic: submit wallet signature after approval to create the on-chain subscription.
01Content-Type: application/json01{02 "customerAddress": "0x742d35Cc6634C0532925a3b844Bc454e4438f44e",03 "deadline": 1751925600,04 "signupSignature": "0x..."05}Create subscriptions directly when you already know the customer wallet: for example after KYC in your app. The customer still must approve the billing token on-chain before charges succeed.
| Field | Type | Required | Description |
|---|---|---|---|
chain | string | No | Chain scope for the request (e.g. base, polygon). Defaults to your dashboard chain selection. Sandbox resolves to testnets automatically. |
planId | uuid | Yes | Active plan to subscribe the customer to. |
customerAddress | address | Yes | Customer wallet that will approve the billing token and sign on-chain subscription consent. |
customerId | uuid | No | Optional CRM customer to attach. Populates subscription.customer in responses and webhooks. |
reference | string | No | Your subscription reference for reconciliation (unique per business). |
metadata | object | No | Arbitrary JSON stored on the subscription and included in webhooks. |
authorization | object | No | Dashboard-only step-up auth when calling from the browser with a JWT session. API key integrations omit this. Supports pin, otpChallengeId, and twoFactorCode. On sandbox, any emailed OTP code is `000000`. |
authorization.pin | string | No | Transaction PIN when enabled for your business. |
authorization.otpChallengeId | uuid | No | OTP challenge ID from a prior dashboard auth flow. On sandbox, verify the emailed code with `000000`. |
authorization.twoFactorCode | string | No | TOTP code when 2FA is enabled. |
/subscriptionsCreate a subscription for a wallet address. Emits subscription.created when confirmed on-chain.
01x-api-key: sk_AbCdEfGhIj_XyZ1234567890abcdefghijklmnopqrstuvwxyz01234502Content-Type: application/json01{02 "planId": "pln_7f3a9b2c-4d1e-4a8f-9c0b-1e2d3f4a5b6c",03 "customerAddress": "0x742d35Cc6634C0532925a3b844Bc454e4438f44e",04 "customerId": ":cus_3b7e9f1a-2c4d-5e6f-7a8b-9c0d1e2f3a4b",05 "reference": "sub-ref-1042",06 "metadata": {07 "tier": "pro"08 }09}| Field | Type | Required | Description |
|---|---|---|---|
chain | string | No | Chain scope for the request (e.g. base, polygon). Defaults to your dashboard chain selection. Sandbox resolves to testnets automatically. |
status | enum | No | Query filter: active, past_due, or cancelled. |
planId | uuid | No | Query filter: only subscriptions for this plan. |
customerId | uuid | No | Query filter: subscriptions linked to this CRM customer. |
customerAddress | address | No | Query filter: subscriptions for a specific wallet. |
authorization | object | No | Dashboard-only step-up auth when calling from the browser with a JWT session. API key integrations omit this. Supports pin, otpChallengeId, and twoFactorCode. On sandbox, any emailed OTP code is `000000`. |
authorization.pin | string | No | Transaction PIN when enabled for your business. |
authorization.otpChallengeId | uuid | No | OTP challenge ID from a prior dashboard auth flow. On sandbox, verify the emailed code with `000000`. |
authorization.twoFactorCode | string | No | TOTP code when 2FA is enabled. |
/subscriptions?status=active&limit=20List subscriptions. Filter by status (active, past_due, cancelled), planId, customerId, or customerAddress.
01x-api-key: sk_AbCdEfGhIj_XyZ1234567890abcdefghijklmnopqrstuvwxyz01234502Content-Type: application/json/subscriptions/sub_5c8d2e1f-9a3b-4c7d-8e6f-1a2b3c4d5e6fRetrieve a subscription. Active and past_due records are reconciled with on-chain state before returning.
01x-api-key: sk_AbCdEfGhIj_XyZ1234567890abcdefghijklmnopqrstuvwxyz01234502Content-Type: application/json/subscriptions/sub_5c8d2e1f-9a3b-4c7d-8e6f-1a2b3c4d5e6f/cancelMerchant-initiated cancel (relayer-signed on-chain). Emits subscription.cancelled.
01x-api-key: sk_AbCdEfGhIj_XyZ1234567890abcdefghijklmnopqrstuvwxyz01234502Content-Type: application/json01{}/subscriptions/sub_5c8d2e1f-9a3b-4c7d-8e6f-1a2b3c4d5e6f/chargeForce a charge when the subscription is due. Fails if cancelled or not yet due unless forced by lifecycle rules.
01x-api-key: sk_AbCdEfGhIj_XyZ1234567890abcdefghijklmnopqrstuvwxyz01234502Content-Type: application/json01{}Portal links (portal-*) let customers view billing history, pay overdue balances, cancel, and reactivate: without your API key. portalUrl points to https://videpay.io/subscriptions/{code}.
| Field | Type | Required | Description |
|---|---|---|---|
chain | string | No | Chain scope for the request (e.g. base, polygon). Defaults to your dashboard chain selection. Sandbox resolves to testnets automatically. |
expiresInDays | integer | No | Portal link expiry in days (1–3650). Omit for a permanent link. Re-calling the endpoint refreshes expiry when provided. |
/subscriptions/sub_5c8d2e1f-9a3b-4c7d-8e6f-1a2b3c4d5e6f/public-linkGenerate or refresh a customer portal link for a subscription.
01x-api-key: sk_AbCdEfGhIj_XyZ1234567890abcdefghijklmnopqrstuvwxyz01234502Content-Type: application/json01{02 "expiresInDays": 3003}/subscriptions/public/portal-f9e8d7c6b5a4321098765432Public: load subscription status, billing history, reactivation availability, and portal actions.
Customers interact with these endpoints from the hosted portal after connecting a wallet. Each flow follows prepare → sign/submit.
/subscriptions/public/portal-f9e8d7c6b5a4321098765432/pay/preparePrepare token approval and paySubscription contract call for overdue balance.
/subscriptions/public/portal-f9e8d7c6b5a4321098765432/paySubmit payment after approval: forces a charge job.
/subscriptions/public/portal-f9e8d7c6b5a4321098765432/cancel/prepareReturn EIP-712 typed data for customer-signed cancellation.
/subscriptions/public/portal-f9e8d7c6b5a4321098765432/cancelSubmit cancelSignature to cancel on-chain.
01{02 "deadline": 1751925600,03 "cancelSignature": "0x..."04}/subscriptions/public/portal-f9e8d7c6b5a4321098765432/reactivate/preparePrepare allowance + typed data to resume a cancelled subscription.
01{02 "customerAddress": "0x742d35Cc6634C0532925a3b844Bc454e4438f44e"03}/subscriptions/public/portal-f9e8d7c6b5a4321098765432/reactivateSubmit reactivateSignature after approval. Reactivation is portal-only, there is no merchant POST /reactivate endpoint.
01{02 "customerAddress": "0x742d35Cc6634C0532925a3b844Bc454e4438f44e",03 "deadline": 1751925600,04 "reactivateSignature": "0x..."05}No pause: only active, past_due, and cancelled
Subscriptions cannot be paused. A failed charge moves status to past_due. Videpay retries up to three times with exponential backoff. After retries exhaust, the subscription is cancelled automatically. Use manual charge or customer portal pay to recover past_due subscriptions before cancellation.
Every subscription.* webhook and GET response uses this shape (via serializeSubscription). The customer field is always present: null when no customerId was provided at signup.
| Field | Type | Required | Description |
|---|---|---|---|
id | uuid | No | Videpay subscription record ID. |
subscriptionHash | bytes32 | No | On-chain subscription identifier hash. |
customerAddress | address | No | Wallet charged each billing cycle. |
customer | object | null | No | Linked CRM customer when customerId was set at creation or checkout collected buyer identity. Always present on deposit sessions, settlement payments, and subscriptions: null when unlinked. Shape: { id, name, email, phoneNumber, reference }. |
id | uuid | No | Videpay customer record ID. |
name | string | No | Display name. |
email / phoneNumber | string | null | No | Contact fields when collected. |
reference | string | null | No | Your CRM or user ID: unique per business. |
reference / metadata | string / object | No | Your reconciliation fields: echoed in webhooks. |
status | enum | No | active, past_due, or cancelled. |
billing.amountRaw / billing.amount | string | No | Recurring charge in raw and formatted units. |
billing.interval / intervalSeconds | string / integer | No | Billing cadence label and fixed-second interval. |
nextChargeAt | datetime | No | Next scheduled charge. null when cancelled. |
transaction.*TxHash | string | No | On-chain transaction hashes for create, last charge, and cancellation. |
plan | object | No | Embedded serialized plan at time of response. |
Register a webhook endpoint and verify the X-Protocol-Signature header on every delivery before fulfilling orders or updating your database.
subscription.plan.createdSent when you create a subscription plan.
01Content-Type: application/json02User-Agent: Protocol-Webhooks/1.003X-Protocol-Event: subscription.plan.created04X-Protocol-Delivery: wev_1a2b3c4d-5e6f-7890-abcd-ef123456789005X-Protocol-Business-Id: a1b2c3d4-e5f6-7890-abcd-ef123456789006X-Protocol-Attempt: 107X-Protocol-Signature: sha256=abc123def456...subscription.plan.deactivatedSent when you deactivate a plan (existing subscriptions are not deleted).
01Content-Type: application/json02User-Agent: Protocol-Webhooks/1.003X-Protocol-Event: subscription.plan.deactivated04X-Protocol-Delivery: wev_1a2b3c4d-5e6f-7890-abcd-ef123456789005X-Protocol-Business-Id: a1b2c3d4-e5f6-7890-abcd-ef123456789006X-Protocol-Attempt: 107X-Protocol-Signature: sha256=abc123def456...subscription.createdSent when a subscription is created on-chain (merchant API or public signup).
01Content-Type: application/json02User-Agent: Protocol-Webhooks/1.003X-Protocol-Event: subscription.created04X-Protocol-Delivery: wev_1a2b3c4d-5e6f-7890-abcd-ef123456789005X-Protocol-Business-Id: a1b2c3d4-e5f6-7890-abcd-ef123456789006X-Protocol-Attempt: 107X-Protocol-Signature: sha256=abc123def456...subscription.chargedSent when a recurring charge succeeds and the billing window advances.
01Content-Type: application/json02User-Agent: Protocol-Webhooks/1.003X-Protocol-Event: subscription.charged04X-Protocol-Delivery: wev_1a2b3c4d-5e6f-7890-abcd-ef123456789005X-Protocol-Business-Id: a1b2c3d4-e5f6-7890-abcd-ef123456789006X-Protocol-Attempt: 107X-Protocol-Signature: sha256=abc123def456...subscription.past_dueSent when a charge fails and the subscription enters past_due.
01Content-Type: application/json02User-Agent: Protocol-Webhooks/1.003X-Protocol-Event: subscription.past_due04X-Protocol-Delivery: wev_1a2b3c4d-5e6f-7890-abcd-ef123456789005X-Protocol-Business-Id: a1b2c3d4-e5f6-7890-abcd-ef123456789006X-Protocol-Attempt: 107X-Protocol-Signature: sha256=abc123def456...subscription.cancelledSent when a subscription is cancelled by merchant, customer portal, or max retries.
01Content-Type: application/json02User-Agent: Protocol-Webhooks/1.003X-Protocol-Event: subscription.cancelled04X-Protocol-Delivery: wev_1a2b3c4d-5e6f-7890-abcd-ef123456789005X-Protocol-Business-Id: a1b2c3d4-e5f6-7890-abcd-ef123456789006X-Protocol-Attempt: 107X-Protocol-Signature: sha256=abc123def456...subscription.reactivatedSent when a cancelled or past_due subscription becomes active again.
01Content-Type: application/json02User-Agent: Protocol-Webhooks/1.003X-Protocol-Event: subscription.reactivated04X-Protocol-Delivery: wev_1a2b3c4d-5e6f-7890-abcd-ef123456789005X-Protocol-Business-Id: a1b2c3d4-e5f6-7890-abcd-ef123456789006X-Protocol-Attempt: 107X-Protocol-Signature: sha256=abc123def456...