Hold A Payout Until Confirmed
Hold merchant payout funds in escrow until a confirmation event arrives, then release the merchant share with a wait_event step.
A two-leg payout where the second leg is held until an external system confirms delivery. The platform sends the gross amount to an escrow wallet first, then waits for a delivery.confirmed event before releasing the merchant share.
Problem
Your marketplace wants to pay merchants only after the buyer has confirmed receipt of goods (or after a fraud check passes). Until that confirmation arrives, the merchant share should be held; if the confirmation never arrives, the workflow should fail loudly so an operator can investigate.
Workflow definition
version: 1
name: "marketplace-escrow"
description: "Hold the merchant share until the buyer confirms delivery"
steps:
- name: "send_to_escrow"
send_money:
source:
type: "wallet"
account: "${input.platform_wallet}"
destination:
type: "wallet"
account: "${input.escrow_wallet}"
account_name: "Escrow Holding"
bic: "PAEYPHM2XXX"
provider: "paymongo"
amount: "${input.gross_amount}"
currency: "PHP"
notes: "Order ${input.order_id} (escrow)"
- name: "wait_for_confirmation"
wait_event:
event_name: "delivery.confirmed"
timeout: "P3D"
on_timeout: "fail"
payload_schema:
type: "object"
required: ["order_id"]
properties:
order_id:
type: "string"
- name: "release_to_merchant"
send_money:
source:
type: "wallet"
account: "${input.escrow_wallet}"
destination:
type: "wallet"
account: "${input.merchant_wallet}"
account_name: "${input.merchant_name}"
bic: "PAEYPHM2XXX"
provider: "paymongo"
amount: "${input.merchant_share}"
currency: "PHP"
notes: "Order ${steps.wait_for_confirmation.output.event_payload.order_id} (release)"The wait_event step pauses the workflow for up to three days. If no delivery.confirmed event arrives in that window, on_timeout: fail ends the instance in failed status, so your ops dashboard can flag stuck escrow positions to reconcile.
The payload_schema rejects events that do not carry an order_id, which means the third step can safely reference ${steps.wait_for_confirmation.output.event_payload.order_id} without further validation.
Trigger
The workflow runs every time an order is created. Subscribe to whatever event your application publishes for new orders:
curl --request POST 'https://workflow-api.paymongo.com/v1/triggers' \
--header 'Authorization: Basic ${YOUR_BASIC_TOKEN}' \
--header 'Organization-Id: ${YOUR_ORG_ID}' \
--header 'Content-Type: application/json' \
--data '{
"workflow_id": "wf_marketplace",
"condition": {
"event_name": "marketplace.order.created"
}
}'Confirming an order is a separate API call against the running instance:
curl --request POST 'https://workflow-api.paymongo.com/v1/instances/${INSTANCE_ID}/events' \
--header 'Authorization: Basic ${YOUR_BASIC_TOKEN}' \
--header 'Organization-Id: ${YOUR_ORG_ID}' \
--header 'Content-Type: application/json' \
--data '{
"event_name": "delivery.confirmed",
"payload": {
"order_id": "ord_abc123"
}
}'To find which instance to confirm, use GET /v1/instances/waiting?event_name=delivery.confirmed.
What success looks like
A confirmed run shows three completed steps (send_to_escrow, wait_for_confirmation with event_received: true, and release_to_merchant):
{
"data": {
"instance_id": "inst_xyz",
"status": "completed",
"output": {
"steps_executed": 3,
"step_outputs": [
{"status": "completed", "transfer_id": "tr_escrow"},
{"status": "completed", "event_received": true, "event_payload": {"order_id": "ord_abc123"}},
{"status": "completed", "transfer_id": "tr_release"}
]
}
}
}If the timeout elapses, the instance ends in failed status with the second step's status set to timed_out. The escrow funds remain on the escrow wallet, and your ops process should reclaim or refund them manually.
Variations
- Soft hold. Switch
on_timeout: "fail"toon_timeout: "continue"if you want the workflow to release the funds anyway after the timeout. The third step runs unconditionally. - Different timeout.
PT24H(24 hours),P7D(7 days, the maximum), or any ISO 8601 duration up to seven days. - Stricter payload validation. Expand
payload_schemato require additional fields likeconfirmed_byorconfirmation_method. - Multiple confirmation attempts. Set
max_retries: 2to allow up to three timeout windows in sequence (total wait still capped at 7 days).
See also
- Wait Event Step: full parameter and output reference.
- Submit an Event: API reference for the confirmation call.
- Observing Workflow Runs: find waiting instances.
Updated about 5 hours ago