Skip to content

Treasury Events

Treasury operations emit webhook events when the status of deposits, withdrawals, transfers, payments, and wallets changes.

Event names use withdraw (not withdrawal). Subscribe to the names below — these match what TurnStay emits and what appears in the webhook subscription UI.

EventFires when
treasury.deposit.createdDeposit created.
treasury.deposit.pendingDeposit is pending.
treasury.deposit.completedDeposit funds have been credited to a wallet.
treasury.deposit.cancelledDeposit cancelled.
treasury.withdraw.createdWithdrawal created.
treasury.withdraw.pendingWithdrawal is pending.
treasury.withdraw.completedWithdrawal has been sent to the bank account.
treasury.withdraw.failedWithdrawal processing failed.
treasury.withdraw.cancelledWithdrawal cancelled.
treasury.transfer.completedInter-wallet transfer completed.
treasury.transfer.failedTransfer processing failed.
treasury.payment.awaiting_claimClaimable payment created; funds reserved until the recipient claims.
treasury.payment.pendingPayment is in flight (for example a bank payout after a claim to bank).
treasury.payment.completedPayment settled (including after a claim is accepted and settlement finishes).
treasury.payment.failedPayment failed.
treasury.payment.cancelledPayment cancelled.
treasury.wallet.createdWallet created.

There is no treasury.payout.* or treasury.withdrawal.* event for claimable wallet payments.

Typical claim flow:

  1. Sender creates a claimable payment → treasury.payment.awaiting_claim
  2. Recipient accepts the claim (wallet or bank) and settlement completestreasury.payment.completed

treasury.payment.completed is the confirmation that funds have been claimed and settled. It fires when the payment reaches a terminal settled state — not necessarily at the instant the recipient clicks claim. For some bank / external claims, settlement can lag; you may also see treasury.payment.pending while a bank payout is in flight.

Events are only delivered if your webhook endpoint is configured correctly:

  1. Subscribe to the exact treasury.payment.* names in the Dashboard (Listening to / Manage event subscriptions). If you are not subscribed, TurnStay may still accept the internal emit, but nothing is POSTed to your URL.
  2. Merchant of Record is separate. Receiving merchant_of_record.payment_intent.succeeded (or other MOR events) on the same URL does not mean treasury claim events are delivered. See Payment Events.
  3. Account ID filter (optional). If the endpoint has an Account ID set, it must match the sender wallet id on the event (or leave Account ID empty). A mismatched Account ID silently drops the delivery.
{
"type": "treasury.payment.completed",
"data": {
"object": {
"id": "pay_abc123",
"company_id": "cmp_xyz",
"wallet_id": "wal_sender789",
"status": "COMPLETE",
"payment_method": "CLAIMABLE",
"from_amount": 100000,
"to_amount": 100000,
"from_currency_code": "ZAR",
"to_currency_code": "ZAR",
"destination_wallet_id": "wal_dest456",
"claim_id": "clm_abc123",
"reference": "TIP-001",
"error": null,
"processed_at": "2026-06-01T10:30:00Z",
"exchange_rate_from_to": null
}
}
}
{
"type": "treasury.withdraw.completed",
"data": {
"object": {
"id": "wd_abc123",
"company_id": "cmp_xyz",
"wallet_id": "wal_xyz789",
"status": "completed",
"from_amount": 100000,
"to_amount": 100000,
"from_currency_code": "ZAR",
"to_currency_code": "ZAR",
"provider_reference": "WD-001",
"error": null,
"processed_at": "2026-06-01T10:30:00Z"
}
}
}
@app.route("/webhooks/turnstay", methods=["POST"])
def turnstay_webhook():
event = request.get_json()
event_type = event["type"]
if event_type == "treasury.deposit.completed":
deposit = event["data"]["object"]
update_deposit_status(deposit["id"], "completed")
elif event_type == "treasury.withdraw.completed":
withdraw = event["data"]["object"]
update_withdrawal_status(withdraw["id"], "completed")
elif event_type == "treasury.payment.awaiting_claim":
payment = event["data"]["object"]
mark_payment_awaiting_claim(payment["id"])
elif event_type == "treasury.payment.pending":
payment = event["data"]["object"]
mark_payment_pending(payment["id"])
elif event_type == "treasury.payment.completed":
payment = event["data"]["object"]
mark_payment_completed(payment["id"])
return jsonify({"ok": True}), 200