Cancel Payout API
Cancel a pending payout and receive a refund to your wallet
Cancel Payout API
Cancel a payout that has not yet been completed. Cancelled payouts are refunded to your wallet.
Endpoint
POST /api/v1/payouts/:id/cancelAuthentication: Bearer token required
Request Headers
Authorization: Bearer <access_token>Path Parameters
| Parameter | Type | Description |
|---|---|---|
id | string | Payout ID to cancel (e.g., pyt_xyz789abc) |
Cancellation Rules
A payout can only be cancelled when it's in certain states:
| Status | Can Cancel? | Notes |
|---|---|---|
created | Yes | Immediately cancellable |
queued | Yes | Can cancel before processing starts |
processing | Maybe | Depends on provider - may already be submitted |
settling | No | Funds already with beneficiary |
action_required | Yes | Can cancel while waiting for action |
completed | No | Already delivered |
failed | No | Already failed (refund automatic) |
cancelled | No | Already cancelled |
expired | No | Already expired |
Response
Success Response
Status Code: 200 OK
Returns the updated payout with cancelled status:
{
"id": "pyt_xyz789abc",
"reference_id": "PAY_2024_001",
"status": "cancelled",
"amount": 100.00,
"currency": "USD",
"fees": 1.50,
"total_amount": 101.50,
"beneficiary": {
"email": "john.doe@example.com",
"first_name": "John",
"last_name": "Doe",
"country": "US"
},
"description": "Bonus Payment",
"created_at": "2024-01-15T10:30:00Z",
"cancelled_at": "2024-01-15T10:32:00Z",
"events": [
{
"event_type": "status_change",
"from_status": null,
"to_status": "created",
"actor_type": "system",
"message": "Payout created",
"created_at": "2024-01-15T10:30:00Z"
},
{
"event_type": "status_change",
"from_status": "created",
"to_status": "cancelled",
"actor_type": "client",
"message": "Payout cancelled by client",
"created_at": "2024-01-15T10:32:00Z"
}
]
}Error Responses
400 Bad Request - Cannot Cancel
{
"error": "BadRequest",
"message": "payout cannot be cancelled in current status"
}400 Bad Request - Already Completed
{
"error": "BadRequest",
"message": "payout has already been completed"
}400 Bad Request - Missing ID
{
"error": "BadRequest",
"message": "Payout ID is required"
}404 Not Found
{
"error": "NotFound",
"message": "Payout not found"
}401 Unauthorized
{
"error": "UnauthorizedAccess",
"message": "Authentication required"
}Examples
Basic Cancellation
curl -X POST "{{host}}/api/v1/payouts/pyt_xyz789abc/cancel" \
-H "Authorization: Bearer your_access_token"<?php
function cancelPayout($accessToken, $payoutId) {
$url = "{{host}}/api/v1/payouts/$payoutId/cancel";
$options = [
'http' => [
'header' => "Authorization: Bearer $accessToken",
'method' => 'POST'
]
];
$context = stream_context_create($options);
$result = file_get_contents($url, false, $context);
if ($result === false) {
// Check response headers for error details
throw new Exception('Failed to cancel payout');
}
return json_decode($result, true);
}
// Usage
try {
$payout = cancelPayout('your_access_token', 'pyt_xyz789abc');
if ($payout['status'] === 'cancelled') {
echo "Payout cancelled successfully!\n";
echo "Refund amount: " . $payout['total_amount'] . " " . $payout['currency'] . "\n";
}
} catch (Exception $e) {
echo "Error: " . $e->getMessage() . "\n";
}
?>Safe Cancellation with Status Check
async function safeCancelPayout(payoutId) {
// First, check current status
const checkResponse = await fetch(`/api/v1/payouts/${payoutId}`, {
headers: {
'Authorization': `Bearer ${getAccessToken()}`
}
});
const payout = await checkResponse.json();
// Check if cancellation is possible
const cancellableStatuses = ['created', 'queued', 'action_required'];
const maybeCancellable = ['processing'];
if (!cancellableStatuses.includes(payout.status) &&
!maybeCancellable.includes(payout.status)) {
throw new Error(`Payout cannot be cancelled - status is ${payout.status}`);
}
if (maybeCancellable.includes(payout.status)) {
console.warn('Payout is processing - cancellation may not succeed');
}
// Attempt cancellation
const cancelResponse = await fetch(`/api/v1/payouts/${payoutId}/cancel`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${getAccessToken()}`
}
});
if (!cancelResponse.ok) {
const error = await cancelResponse.json();
throw new Error(error.message || 'Failed to cancel payout');
}
return await cancelResponse.json();
}
// Usage
try {
const cancelledPayout = await safeCancelPayout('pyt_xyz789abc');
console.log('Payout cancelled:', cancelledPayout.id);
console.log('Refund amount:', cancelledPayout.total_amount, cancelledPayout.currency);
} catch (error) {
console.error('Cancellation failed:', error.message);
}Batch Cancellation
async function cancelMultiplePayouts(payoutIds) {
const results = {
cancelled: [],
failed: []
};
for (const payoutId of payoutIds) {
try {
const payout = await safeCancelPayout(payoutId);
results.cancelled.push({
id: payoutId,
refund: payout.total_amount
});
} catch (error) {
results.failed.push({
id: payoutId,
reason: error.message
});
}
}
return results;
}
// Usage
const results = await cancelMultiplePayouts([
'pyt_abc123',
'pyt_def456',
'pyt_ghi789'
]);
console.log(`Cancelled: ${results.cancelled.length}`);
console.log(`Failed: ${results.failed.length}`);
results.failed.forEach(f => {
console.log(` ${f.id}: ${f.reason}`);
});User Confirmation Flow
async function cancelPayoutWithConfirmation(payoutId) {
// Fetch payout details
const payout = await getPayout(payoutId);
// Show confirmation dialog
const confirmed = await showConfirmDialog({
title: 'Cancel Payout?',
message: `Are you sure you want to cancel this payout?`,
details: [
`Amount: ${payout.amount} ${payout.currency}`,
`Beneficiary: ${payout.beneficiary.first_name} ${payout.beneficiary.last_name}`,
`Reference: ${payout.reference_id}`
],
confirmText: 'Yes, Cancel Payout',
cancelText: 'No, Keep It'
});
if (!confirmed) {
return null;
}
// Proceed with cancellation
const cancelledPayout = await cancelPayout(payoutId);
// Show success message
showNotification({
type: 'success',
message: `Payout cancelled. ${cancelledPayout.total_amount} ${cancelledPayout.currency} refunded to wallet.`
});
return cancelledPayout;
}Refund Process
When a payout is cancelled:
- Status Update - Payout status changes to
cancelled - Wallet Credit - The total amount (payout amount + fees) is refunded to the source wallet
- Event Logged - A cancellation event is added to the payout history
- Timestamp Set - The
cancelled_atfield is populated
Refund Timeline
- Immediate: Cancellation of
createdorqueuedpayouts - May take time: Cancellation of
processingpayouts (depends on provider)
Verify Refund
After cancellation, verify the refund in your wallet:
async function verifyCancellationRefund(payoutId) {
// Get cancelled payout
const payout = await getPayout(payoutId);
if (payout.status !== 'cancelled') {
throw new Error('Payout was not cancelled');
}
// Get wallet transactions
const transactions = await getWalletTransactions({
wallet_id: payout.wallet_id,
type: 'refund',
reference: payoutId
});
const refund = transactions.find(t =>
t.reference_id === payoutId &&
t.type === 'PAYOUT_REFUND'
);
if (refund) {
console.log('Refund confirmed:', refund);
return refund;
} else {
console.log('Refund pending or not found');
return null;
}
}Error Handling
Handle Common Errors
async function handleCancellation(payoutId) {
try {
const payout = await cancelPayout(payoutId);
return {
success: true,
payout
};
} catch (error) {
const errorMessage = error.message.toLowerCase();
if (errorMessage.includes('not found')) {
return {
success: false,
error: 'PAYOUT_NOT_FOUND',
message: 'The payout does not exist or you do not have access to it'
};
}
if (errorMessage.includes('cannot be cancelled') ||
errorMessage.includes('already been completed')) {
return {
success: false,
error: 'CANNOT_CANCEL',
message: 'This payout cannot be cancelled in its current state'
};
}
if (errorMessage.includes('unauthorized')) {
return {
success: false,
error: 'UNAUTHORIZED',
message: 'You are not authorized to cancel this payout'
};
}
return {
success: false,
error: 'UNKNOWN_ERROR',
message: error.message
};
}
}Best Practices
- Check status first - Verify the payout is in a cancellable state before attempting
- Act quickly - Cancel as soon as possible; once processing starts, cancellation may fail
- Confirm with users - Always show a confirmation dialog for user-initiated cancellations
- Verify refunds - Check wallet balance or transaction history to confirm refund
- Log cancellations - Keep records of all cancellation attempts and outcomes
Use Cases
Duplicate Detection
async function handleDuplicatePayout(newReferenceId, duplicatePayoutId) {
console.log(`Duplicate detected: ${duplicatePayoutId}`);
// Check if the duplicate can be cancelled
const duplicate = await getPayout(duplicatePayoutId);
if (['created', 'queued'].includes(duplicate.status)) {
// Cancel the duplicate
await cancelPayout(duplicatePayoutId);
console.log('Duplicate cancelled');
} else {
console.log('Duplicate already processing - manual review needed');
}
}Fraud Prevention
async function emergencyCancel(clientId) {
// Get all pending payouts for a client
const payouts = await listPayouts({
status: 'created',
client_id: clientId
});
// Cancel all pending payouts
const results = await cancelMultiplePayouts(
payouts.map(p => p.id)
);
console.log(`Emergency cancel complete:
Cancelled: ${results.cancelled.length}
Failed: ${results.failed.length}`);
return results;
}Related Endpoints
- Create Payout - Create new payouts
- Get Payout - Check payout status before cancelling
- List Payouts - Find payouts to cancel
- Wallets API - Verify refund in wallet