Recurring billing here is two fields on a normal transaction, not a separate product. There is no subscription resource, no plan, no customer object, and no recurring endpoint. You own the schedule; the gateway records the stored-credential relationship the card networks require and returns the identifiers that link one payment to the next.
Everything goes through POST /v1/transactions.
| Field | What it does |
|---|---|
recurring | Boolean. Marks the transaction as part of a recurring arrangement |
initiator | CUSTOMER when the cardholder is present, MERCHANT when you bill without them |
recurring_details.sequence | INITIAL for the first payment, SUBSEQUENT for every one after |
recurring_details.initial_transaction_id | The id of the transaction that began the series |
recurring_details.initial_network_transaction_id | The network's own identifier, when the series began somewhere else |
The first payment establishes the agreement. The cardholder is present and consenting, so it is customer-initiated.
{
"type": "SALE",
"amount": 10000,
"payment_method": { "type": "Card", "pan": "...", "expiry": { "month": 1, "year": 2028 } },
"initiator": "CUSTOMER",
"recurring": true,
"recurring_details": { "sequence": "INITIAL" }
}From the response, keep two values:
id— quote it asinitial_transaction_idon every later payment.network_transaction_id— the card network's identifier for the agreement. Store it; you need it if you ever move this series to another gateway.
Later payments are merchant-initiated: you are billing a card without the cardholder present. Send no CVV, because nobody is there to read one.
You can send the card details again, as that example does, or send a saved token. Either works; the token is the better choice because it keeps the number out of your systems.
The gateway enforces these and returns 400 with INVALID_ACTION when one is broken. The schema does not describe them, so a generated SDK will not catch them for you — the request looks valid right up until the API rejects it.
| Rule | Message when you break it |
|---|---|
recurring_details present means initiator is required | initiator is required for recurring transactions |
sequence: INITIAL must use initiator: CUSTOMER | initiator must be CUSTOMER for the initial recurring transaction |
sequence: SUBSEQUENT needs initial_transaction_id or initial_network_transaction_id | Either initial_transaction_id or initial_network_transaction_id is required for subsequent recurring transactions |
sequence: INITIAL must omit both of those | initial_transaction_id and initial_network_transaction_id must be omitted on the initial recurring transaction |
The second rule is the one that trips people. An initial recurring payment cannot be merchant-initiated: the cardholder has to be present to consent to their card being stored. If you are setting up a series without the customer there, you do not have an initial payment — you have a subsequent payment on an agreement made somewhere else, and it needs rule three.
If the agreement started elsewhere, you have no initial_transaction_id to give, because the first payment was never a transaction here.
Send the network's identifier instead, in recurring_details.initial_network_transaction_id. That is what the field is for, and it is the only way to keep the stored-credential chain intact across a migration. Losing it usually shows up as a higher decline rate rather than an error.
- Installments are out of scope. The gateway processes recurring payments. Fixed-instalment plans are not offered on the public API.
- No schedule, no retries, no dunning. Nothing here runs on a timer. Your system decides when to bill and what to do when a payment fails.
- No customers, plans, or subscriptions. Those resources do not exist. Your application owns them.