Customers
Your app's users, keyed by your own id, with everything Revenue knows about them.
customers.get_or_create
Call it whenever you see a user, typically on sign in. The first call creates the customer and attaches every free default plan. Later calls update name, email and fingerprint if they changed.
{
"customer_id": "user_123",
"email": "ama@example.com",
"name": "Ama Mensah",
"currency": "GHS",
"fingerprint": "device_8f2c",
"metadata": { "team": "growth" },
"expand": ["invoices", "payment_methods"]
}Only customer_id is required. An email is needed before a paid plan can be attached, because Paystack asks for one. fingerprint (a device or browser id) limits free trials to one per device. currency is set on create only; once a customer has paid, they stay in that currency.
{
"id": "user_123",
"name": "Ama Mensah",
"email": "ama@example.com",
"currency": "GHS",
"env": "test",
"credits": [],
"subscriptions": [
{
"plan_id": "pro",
"status": "active",
"amount": 2000,
"currency": "USD",
"interval": "month",
"quantities": [],
"current_period_end": "2026-11-11T13:00:00.000Z",
"cancel_at_period_end": false,
"scheduled_for": null
}
],
"purchases": [],
"balances": {
"messages": {
"feature_id": "messages",
"type": "metered",
"granted": 150,
"used": 30,
"remaining": 120,
"overage_allowed": false,
"next_reset_at": "2026-11-11T13:00:00.000Z",
"interval": "month",
"breakdown": [{ "id": "plan:pro", "plan_id": "pro", "amount": 100, "interval": "month", "expires_at": null }],
"rollovers": [{ "amount": 50, "expires_at": "2026-12-11T13:00:00.000Z" }],
"usage_limits": [{ "limit": 200, "interval": "day", "usage": 37, "source": "plan" }]
}
},
"flags": { "sso": { "feature_id": "sso", "plan_id": "pro", "expires_at": null } },
"billing_controls": { "usage_limits": [{ "feature_id": "messages", "limit": 200, "interval": "day", "source": "plan" }], "spend_limits": [], "overage_allowed": [], "usage_alerts": [], "auto_topups": [] }
}subscriptionsare recurring plans and add ons, including ascheduledone waiting for renewal.purchasesare one off plans.plansis kept for older integrations and lists both.balancescovers metered features and credit systems. On and off features are inflags, socustomer.flags.ssois all you need to show a button.billing_controlsare the effective limits, withsourcetelling you whether each came from the plan or was set on the customer.creditsis money on account from plan changes; it pays the next invoices first.expandaddsinvoices,entities,payment_methods,rewardsandreferrals.
customers.get
{ "customer_id": "user_123", "expand"?: [...] } returns the same shape. 404 customer_not_found if you never created them.
customers.update
{ "customer_id": "user_123", "name"?, "email"?, "currency"?, "fingerprint"?, "metadata"?, "billing_controls"? }
billing_controls is merged by feature into what the customer already has. Send "replace_billing_controls": true to replace them all. See Limits and alerts.
customers.delete
{ "customer_id": "user_123" } ends every plan now without a refund and hides the customer. Calling customers.get_or_create with the same id brings them back with default plans.
customers.list
{ "search"?: "ama", "plan_id"?: "pro", "status"?: "past_due", "limit"?: 50, "start_cursor"?: "..." } returns { "list": [...], "next_cursor": "..." | null }.
plans.list with a customer
{ "customer_id": "user_123", "entity_id"?: "ws_1" } adds customer_eligibility to every plan, which is what a pricing table needs:
{ "attach_action": "upgrade", "status": null, "canceling": false, "trial_available": true }attach_action is activate, upgrade, downgrade, purchase or none (their current plan).