Refunds

Return all or part of a succeeded payment to the customer.

You can refund a payment whose status is succeeded, in full or in several partial refunds, until the whole amount has been refunded. Refunds go back to the method the customer paid with.

Create a refund

POST /v1/payments/:id/refunds

ParameterTypeRequiredDescription
amountintegerNoAmount to refund in minor units. Omit it to refund everything not yet refunded.
reasonstringNorequested_by_customer (the default), duplicate, fraudulent or other.

Send a JSON body with Content-Type: application/json. An empty object ({}) refunds everything not yet refunded; a POST with no body at all is treated the same way.

Full refund

curl https://api.teyra.co/v1/payments/pay_01JBX4T9W2/refunds \
  -H "Authorization: Bearer $TEYRA_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: refund-order-1042" \
  -d '{}'

Partial refund

Refund 5.000 KWD of a 12.500 KWD payment:

curl https://api.teyra.co/v1/payments/pay_01JBX4T9W2/refunds \
  -H "Authorization: Bearer $TEYRA_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: refund-order-1042-item-3" \
  -d '{ "amount": 5000, "reason": "requested_by_customer" }'

The response is 201 Created with a Refund. The payment's amount_refunded goes up by the refunded amount; its status stays succeeded.

The Refund object

{
  "id": "re_01JBX6M1QK",
  "object": "refund",
  "livemode": false,
  "payment_id": "pay_01JBX4T9W2",
  "amount": 5000,
  "currency": "KWD",
  "status": "pending",
  "reason": "requested_by_customer",
  "created_at": "2026-10-29T09:12:00.000Z",
  "succeeded_at": null,
  "failure_message": null
}
StatusMeaning
pendingAccepted and being processed.
succeededThe money is on its way back to the customer. A refund.succeeded webhook is sent.
failedThe refund could not be completed. See failure_message. A refund.failed webhook is sent, and the amount can be refunded again.
canceledThe refund was canceled before it completed.

How long the money takes to appear on the customer's statement depends on their bank and payment method.

The refunded amount comes out of your balance, and Teyra Pay's commission on the original payment isn't returned. On some accounts, partial refunds in three-decimal currencies (KWD, BHD, OMR, JOD) must end in 0, like payment amounts: see Payment methods and your account.

Rules and errors

CaseError
The payment is not succeeded (for example failed or expired)422 payment_not_refundable
amount is more than amount minus amount_refunded, or nothing is left to refund422 refund_exceeds_amount (details.remaining holds what is left)
amount is zero, negative or not an integer, or reason is not one of the values above422 validation_error
The payment doesn't exist (or belongs to the other mode)404 payment_not_found

Retrieve and list refunds

curl https://api.teyra.co/v1/refunds/re_01JBX6M1QK \
  -H "Authorization: Bearer $TEYRA_SECRET_KEY"

curl -G https://api.teyra.co/v1/refunds \
  -H "Authorization: Bearer $TEYRA_SECRET_KEY" \
  -d payment_id=pay_01JBX4T9W2

An unknown refund ID returns 404 refund_not_found.

You can also refund from the dashboard: open the payment and click Refund.

Disputes

A dispute (or chargeback) is when a cardholder asks their bank to reverse a card, Apple Pay or Google Pay payment, for example because they don't recognise it or say the order never arrived. It is not a refund you started: the cardholder's bank opens it, and the card network decides the outcome.

Whether disputes are reported in your dashboard depends on your account. On accounts where they are, including UAE accounts:

  • Every owner of the account gets a "A payment was disputed" email with the disputed amount, the payment ID and your reference, the reason, and the date to respond by.
  • Refunds → Disputes in the dashboard lists each dispute with its amount, reason, respond-by date and status, and the payment's timeline shows when it opened, each update and how it closed.
  • The disputed amount is held until the dispute is decided. The payment's status stays succeeded and amount_refunded doesn't change.
Dispute statusMeaning
needs_responseOpened. Send your evidence before the respond-by date.
under_reviewEvidence was sent; the cardholder's bank is deciding.
wonDecided in your favour. The held amount is released to you.
lostDecided for the cardholder. The disputed amount is recovered from your balance.
closedClosed without a win or loss decision, for example withdrawn by the cardholder.

On other accounts, Teyra Pay support contacts you when one of your payments is disputed.

What to do:

  • Respond before the respond-by date. Email support@teyra.co with proof of the order: the invoice, proof of delivery or of the service, and your messages with the customer. Teyra Pay submits it to the card network for you.
  • Don't refund a disputed payment. If the cardholder also wins the dispute, they are paid back twice.
  • Reduce disputes: use a clear description (it appears at checkout), a business name customers recognise, and refund quickly when a customer asks.

Disputes are not available in the /v1 API and don't send webhook events yet; follow them in the dashboard.