Events & money movement

Refunds.

Axle never holds your customer's money, so it can't send a refund for you. A refund is a real transaction you send from your own wallet; Axle records it and verifies it on-chain.

How a refund works

  1. Create the refund. Your server calls the API (or you use the dashboard). Axle records a refund as pending and looks up the customer's own sending address from the original on-chain payment.
  2. Send it from your wallet. The dashboard's guided refund page pre-fills the exact amount and the destination address, so nothing is copied by hand.
  3. Axle verifies it. The refund is only marked success after Axle independently confirms the transaction on-chain. It never trusts a merchant's claim that a refund was sent.
  4. Axle sends a refund.processed webhook, and the original payment becomes partially_refunded or refunded.

Create a refund

Refunds move money, so the request requires an Idempotency-Key header. Retrying the same request with the same key returns the original response instead of creating a duplicate.

curl
curl -X POST https://axle-production-fa2b.up.railway.app/v1/payments/<paymentId>/refunds \
  -H "Authorization: Bearer $AXLE_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{ "amountMinor": 3000, "reason": "requested_by_customer" }'
FieldNotes
amountMinorOptional. Omit to refund the entire remaining balance. Minor units of the payment's currency.
reasonOptional free text, for your own records.
response 201
{
  "object": "refund",
  "id": "rf_…",
  "transactionId": "9d94…",
  "amountMinor": 3000,
  "status": "pending",
  "reason": "requested_by_customer",
  "refundToAddress": "0x71C7…976F",
  "refundTxHash": null,
  "failureReason": null,
  "livemode": true
}

refundToAddress is looked up from the original payment's on-chain sender and can be null if that lookup wasn't possible. It never blocks creating the refund. refundTxHash stays null until the refund is verified.

Retrieve a refund

request
GET /v1/refunds/:id
Authorization: Bearer sk_test_...

Errors

StatuscodeCause
400transaction_not_refundableThe payment isn't success or partially_refunded, for example it's still pending or it failed.
400refund_amount_exceeds_remainingThe amount is more than what's left to refund.
404transaction_not_foundDoesn't exist, or belongs to a different merchant.

Other errors follow the shared format in Errors & limits.