Skip to content

Recurring payments

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.

The fields

FieldWhat it does
recurringBoolean. Marks the transaction as part of a recurring arrangement
initiatorCUSTOMER when the cardholder is present, MERCHANT when you bill without them
recurring_details.sequenceINITIAL for the first payment, SUBSEQUENT for every one after
recurring_details.initial_transaction_idThe id of the transaction that began the series
recurring_details.initial_network_transaction_idThe network's own identifier, when the series began somewhere else

Step 1: The initial payment

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 as initial_transaction_id on 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.

Step 2: Every payment after

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 four rules

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.

RuleMessage when you break it
recurring_details present means initiator is requiredinitiator is required for recurring transactions
sequence: INITIAL must use initiator: CUSTOMERinitiator must be CUSTOMER for the initial recurring transaction
sequence: SUBSEQUENT needs initial_transaction_id or initial_network_transaction_idEither initial_transaction_id or initial_network_transaction_id is required for subsequent recurring transactions
sequence: INITIAL must omit both of thoseinitial_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.

Moving a series from another gateway

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.

What this does not include

  • 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.

Next steps