Refunds

A refund returns some or all of a completed payment to the customer. One mutation, paymentRefund, refunds a payment whichever provider took it: it reads the provider from the payment and issues the refund there, so you never need to know how a payment was processed to reverse it.

Refunds need the refund permission, which every organization API key holds for its own organization and every agency API key holds for the organizations under its agency.

Issuing a refund

mutation {
  paymentRefund(
    input: {
      payment: "PAYMENT_ID"
      amount: "25.00"
      reference: "order-1042-return"
      idempotencyKey: "0b7f1c2e-6c3f-4a09-9b7f-2d3e9c1a5e10"
    }
  ) {
    id
    payment
    provider
    amount
    currency
    status
    processorReference
    requestedAt
  }
}
  • payment is the id of the payment to refund.
  • amount is optional. Omit it to refund everything still refundable on the payment. An amount larger than what is left is refused.
  • reference is optional and is yours: use it to tie the refund to a return, a ticket, or an order in your own records.
  • idempotencyKey is optional but strongly recommended. See retrying safely.

What a successful response means

The status in the response depends on the provider, because providers refund at different speeds.

Provider Status returned What it means
NMI COMPLETED The refund was approved on the spot.
CLP RECEIVED The refund was accepted and will settle shortly. It becomes COMPLETED once the provider confirms it.
Plaid Plaid payments cannot be refunded through the API yet. The call fails with a validation error.

A refund the provider refuses is returned as an error rather than as a result with a FAILED status, so a successful response always means the refund was accepted. The error message carries the provider's response code, which you can quote to support.

Retrying safely

A refund is money leaving your account, so a request that times out is the dangerous case: you do not know whether it went through, and repeating it could refund the customer twice.

Send an idempotencyKey with every refund. Any unique string works; a UUID is a good choice. Repeating a call with the same key and the same organization is answered exactly as the first call was: it returns the refund the first call created, or repeats its rejection, and never issues a second refund. A repeat that names a different payment or amount is refused with a conflict error, so a key reused by mistake surfaces as a bug instead of a silent wrong answer.

If a call fails with a message saying the refund may or may not have been issued, do not retry with a new key. Repeat the call with the same key, and what happens depends on the provider. For a CLP payment the repeat either returns the refund that went through or resumes the one that did not, because CLP recognizes the original attempt. For an NMI payment the repeat tells you the outcome is still unknown rather than trying again, because NMI cannot make a second attempt safe. In either case, check the payment before doing anything else.

Partial refunds

Pass an amount smaller than the payment to refund part of it. A payment can be refunded more than once until the refunded total reaches the amount captured; each refund is checked against what is still refundable at the moment it is issued.