Skip to main content
Version: 2.0.0

API reference

Authentication​

Bearer token in the Authorization header: Authorization: Bearer $ION_SECRET_KEY. Test-mode keys (sk_test_...) and live-mode keys (sk_live_...) are separate credentials — a test key can never move real money, even by accident.

Create a charge​

POST /v1/ion/charges

Request

{
"amount": 4200,
"currency": "usd",
"customer": "cus_h82ndk3",
"payment_method": "pm_1a2b3c",
"idempotency_key": "order_9f3e-attempt-1"
}

Response — 200 OK

{
"id": "ch_3P8kq2Aa",
"amount": 4200,
"currency": "usd",
"status": "succeeded",
"customer": "cus_h82ndk3",
"created": "2026-07-20T09:14:22Z"
}

status is one of succeeded, pending (some local payment methods settle asynchronously), or failed.

Create a customer​

POST /v1/ion/customers

{
"email": "jordan@acme.com",
"payment_method": "pm_1a2b3c"
}

Response includes the customer id (cus_...) to reuse on future charges and subscriptions.

Create a subscription​

POST /v1/ion/subscriptions

{
"customer": "cus_h82ndk3",
"plan": "plan_pro_monthly",
"idempotency_key": "sub_acme_pro-2026-07"
}

Response

{
"id": "sub_7k2p9x",
"status": "active",
"current_period_end": "2026-08-20T09:14:22Z"
}

Issue a refund​

POST /v1/ion/refunds

{
"charge": "ch_3P8kq2Aa",
"amount": 2000
}

Omit amount to refund the charge in full. Partial refunds can be issued multiple times against the same charge as long as their total doesn't exceed the original amount.

Errors​

HTTP statuscodeMeaning
400invalid_currencyCurrency not supported, or amount invalid for that currency's subunit
402card_declinedThe payment method was declined by the issuer
404no_such_chargeThe referenced charge/customer/payment_method ID doesn't exist
409idempotency_key_reusedSame key used with a different request body than the original
429rate_limitedBack off using the Retry-After header

card_declined responses include a decline_code (insufficient_funds, expired_card, generic_decline, …) — surface this to the end user rather than a generic "payment failed" message where possible; it materially reduces retry-and-fail loops at checkout.

Rate limits​

100 requests/second per API key across all endpoints. Charge creation specifically is additionally capped at 25/second per key to limit the blast radius of a runaway retry loop.

Support​

For questions about Ion, contact the product team or visit the community forum.