Merchant API

Cancel a payout intent

POST /payout_intents/:id/cancel while the payout is still awaiting approval.

Cancel a payout intent that is still in the approval gate.

Cancellation is only valid while status is requires_approval. Once the payout is processing or terminal, this endpoint returns 422 approval_required and the payout proceeds.

Endpoint

POST /payout_intents/:id/cancel

Required headers:

  • Authorization: Basic <base64(publicKey:secretKey)>.
  • Content-Type: application/json.

Optional but recommended:

  • Idempotency-Key: <uuid>.

Request

curl -s -X POST "$API_BASE/payout_intents/word_01HZX.../cancel" \
  -u "$API_PUBLIC_KEY:$API_SECRET_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{ "reason": "duplicate_request" }'
import { randomUUID } from "node:crypto";

const auth =
  "Basic " +
  Buffer.from(`${process.env.API_PUBLIC_KEY}:${process.env.API_SECRET_KEY}`).toString("base64");

async function cancelPayoutIntent(id: string, reason?: string) {
  const res = await fetch(`${process.env.API_BASE}/payout_intents/${id}/cancel`, {
    method: "POST",
    headers: {
      Authorization: auth,
      "Idempotency-Key": randomUUID(),
      "Content-Type": "application/json",
    },
    body: JSON.stringify(reason ? { reason } : {}),
  });
  if (!res.ok) {
    const { error } = (await res.json()) as { error: { code: string; message: string } };
    throw new Error(`${error.code}: ${error.message}`);
  }
  return res.json();
}

The body carries one optional field, reason — free text up to 512 characters, persisted to the order and surfaced in the dashboard, audit log, and terminal webhook.

Success

{
  "object": "payout_intent",
  "id": "word_01HZX...",
  "amount": "50000",
  "currency": "MYR",
  "country": "MY",
  "environment": "test",
  "status": "cancelled",
  "merchant_reference": "payout_98765",
  "recipient": {
    "type": "bank_account",
    "account_last4": "7890",
    "bank_code": "MBBEMYKL",
    "holder_name": "Alice Tan"
  },
  "failure_code": "cancelled",
  "failure_message": "duplicate_request",
  "created_at": "2026-05-09T12:00:00.000Z",
  "updated_at": "2026-05-09T12:00:03.000Z"
}

A 202 returns the refreshed payout intent after the platform marks it cancelled. The response includes failure_code: "cancelled" and the cancellation reason in failure_message.

Response fields

Prop

Type

Poll GET /payout_intents/:id or watch for payout_intent.cancelled to confirm the terminal state.

Error: 422 approval_required

{
  "error": {
    "code": "approval_required",
    "message": "Payout intent is in status \"processing\"; only requires_approval intents can be cancelled via this endpoint."
  }
}

Other outcomes:

  • 404 not_found — the id does not exist, belongs to another merchant, or is in the other environment.
  • 409 idempotency_conflict — same Idempotency-Key, different body.

On this page