POST /v1/admin/referrals/payouts/{id}/failed — Mark payout as failed
https://api.triport.io/v1/admin/referrals/payouts/4f2c9b1e-7a3d-4c0b-9e21-3f8a6d5c1b22/failedOperator action that transitions a referral payout into the terminal `failed` state, optionally recording a reason.
This is an operator-only endpoint for the referral payout lifecycle. A payout
moves through requested → processing → sent (or failed / cancelled).
When an out-of-band transfer cannot be completed, an operator calls this endpoint
to mark the payout as failed so the user's balance and reporting reflect the
real state.
The transition is only valid from an actionable state. If the payout has already
reached a terminal status (e.g. it was previously marked sent), the request is
rejected with 409 payout_not_actionable rather than silently overwriting it.
An optional free-text reason may be supplied to annotate why the payout failed.
On success the endpoint returns { "ok": true }.
This endpoint requires the X-Admin-Token header. It is not part of the
session-cookie–authenticated user referral surface — it sits under
/v1/admin/referrals/* alongside the other operator controls.
Parameters
Path parameters
idUUIDrequired400 invalid_payout_id.Request bodyobjectreasonstringoptional{} if not needed. Body is capped at 4 KiB.Response
Response fields
| Field | Type | Description |
|---|---|---|
ok | boolean | Always true when the payout was transitioned to failed. |
Errors
All errors use the shared envelope { "error": "<tag>" }. See errors.md
for the full envelope.
| Code | Tag | When it happens |
|---|---|---|
| 400 | invalid_payout_id | The {id} path segment is not a valid UUID. |
| 400 | invalid_json | The request body could not be decoded (malformed JSON or larger than 4 KiB). |
| 401 | admin_token_unset | The operator token is not configured on the server. |
| 401 | admin_token_invalid | The X-Admin-Token header is missing or does not match. |
| 404 | payout_not_found | No payout exists with the given ID. |
| 404 | not_found | The path is missing the action segment (e.g. /payouts/{id} with no /failed). |
| 405 | method_not_allowed | A method other than POST was used. |
| 409 | payout_not_actionable | The payout is in a terminal/non-actionable state and cannot be marked failed. |
| 500 | internal | Unexpected server-side error. |
Examples
JavaScript (fetch)
const res = await fetch(
`https://api.triport.io/v1/admin/referrals/payouts/${payoutId}/failed`,
{
method: "POST",
headers: {
"X-Admin-Token": process.env.TRIPORT_ADMIN_TOKEN,
"Content-Type": "application/json",
},
body: JSON.stringify({ reason: "destination wallet rejected the transfer" }),
}
);
if (!res.ok) {
const { error } = await res.json();
throw new Error(`mark-failed failed: ${error}`);
}
const { ok } = await res.json(); // ok === trueTypeScript SDK (@triport/sdk)
import { TriportAdmin } from "@triport/sdk";
const admin = new TriportAdmin({ adminToken: process.env.TRIPORT_ADMIN_TOKEN! });
await admin.referrals.payouts.markFailed(payoutId, {
reason: "destination wallet rejected the transfer",
});Python (triport-sdk)
import os
from triport import TriportAdmin
admin = TriportAdmin(admin_token=os.environ["TRIPORT_ADMIN_TOKEN"])
admin.referrals.payouts.mark_failed(
payout_id,
reason="destination wallet rejected the transfer",
)Notes
- Idempotency: the transition is guarded — a payout that has already been
resolved (for example marked
sent) returns409 payout_not_actionable, so repeated calls will not flip a settled payout. - Related endpoints:
- Mark payout as sent — the success counterpart,
which records a
tx_hash. - List payouts — read-side list of payouts and their status.
- Operator user view — operator overview for a single user.
- Mark payout as sent — the success counterpart,
which records a
- The
reasonis stored as an annotation only; it does not affect the resulting status, which is alwaysfailedon success.