Adding escrow to a freelance or services platform with one API call
Every services platform hits the same wall. The client does not want to pay a stranger before the work exists, and the freelancer does not want to deliver to a stranger who might not pay. Platforms solve it by holding the money themselves, which means a payment provider, seller onboarding, a balance and a dispute desk. If your clients can pay in USDC or USDT, there is a lighter way: one server-side request per milestone, and a smart contract holds the money until the work is accepted.
The flow in four steps
When a milestone is accepted, your server creates an escrow checkout for it and sends the client there. The client funds escrow from their own wallet, and the funds lock in the escrow contract. Your platform reads the status, sees it is funded, and tells the freelancer to start. When the client accepts the work they release, the freelancer is paid, and your status flips to released. If they disagree, either side opens a dispute and a Vaultion reviewer rules on the evidence.
The platform never holds the money, never onboards the freelancer as a payee and never adjudicates. It creates checkouts and reads statuses.
Step 1: create the checkout
Your server posts to the sessions endpoint with your platform’s URL and name, an order reference, the milestone title, the price in US cents, escrow set to required, the freelancer’s wallet address as the receiving address, and a return URL on your own domain. The response carries a session ID and a checkout path; redirect the client there. There is no account and no API key: the checkout identity is derived from your site and the addresses you send, and browser calls from other origins are refused, so make the call from your server.
Store the order reference before you redirect. The same reference with the same settings always returns the same checkout, so a timed-out call is safe to retry. Put a random part in the reference, such as the milestone ID plus a few random characters, so that nobody can guess it and open that checkout first.
Step 2: map the statuses to your project states
Reading the session gives you one of a small set of statuses. Unpaid means no confirmed payment yet: keep the milestone waiting. Escrow funded means the client has paid into the contract: the freelancer can start, and the freelancer has not been paid yet. Escrow disputed means a dispute is open: hold the milestone. Escrow pending means a ruling was given and its challenge window is running; funds have not moved. Escrow released means the freelancer was paid or can claim the full amount: close the milestone. Escrow refunded means the client was refunded or can claim it back. Escrow closed means the amount was split between the two. Escrow late means the client funded after the checkout’s price deadline: review before telling the freelancer to start.
A status changes only when a transaction confirms on chain. Never mark a milestone funded because the client came back to your return URL or pasted a transaction hash; both are claims, not proof.
Step 3: poll, because there are no webhooks yet
Poll the session every 30 to 60 seconds while the client is on the checkout, then every few minutes until the escrow resolves. That is cheap, because a milestone changes state a handful of times over its life, and it keeps a single source of truth: your database reflects what the chain says, not what a callback said it said.
What you do not have to build
Custody: the escrowed stablecoins sit in an open-source contract on chain, not with Vaultion and not with you. Disputes: either party opens one from the escrow page and a Vaultion reviewer decides on the evidence at no charge. Notifications: the client and the freelancer are emailed at every escrow step automatically, with the freelancer’s email taken from the store email you send. Payee onboarding: the freelancer is a wallet address.
What to disclose to your users
Say it plainly in your own terms. The escrow fee is paid by the client when funding: 4% below 500 USDC, 3% from 500 and 2% from 5,000. A milestone has to be at least $50. Clients pay in USDC or USDT on Ethereum mainnet, Arbitrum One, Base, BNB Smart Chain, TRON, and Solana. Disputes go to Vaultion-assisted human arbitration, which is not decentralized: your users are trusting Vaultion’s reviewers, and a separate guardian can pause a ruling but never redirect funds. There is no sandbox; every network carries real payments, so test with a real $50 milestone.
Your platform’s commission is not taken inside the escrow; only Vaultion’s fee is. Charge your own fee the way you already do, before or after the milestone.
See it for yourself
Vaultion runs on a published, open-source contract that holds the funds — not Vaultion. You can read it and the locked balance on the block explorer before you send anything.
Frequently asked
Does the client need a Vaultion account?
No. The client pays from their own wallet on the hosted checkout. Neither your platform, the client nor the freelancer creates an account or an API key.
When is the freelancer paid?
When the client releases after accepting the work, or when the freelancer claims after the review period if the client goes quiet. Escrow funded means the money is locked, not that it has moved.
How do I know the milestone is funded without webhooks?
Read the session from your server. The status is read from the chain and changes only on a confirmed transaction, so poll it: every 30 to 60 seconds during checkout, then every few minutes.
Can my platform take its commission from the escrow?
No. Only Vaultion’s escrow fee is deducted, paid by the client. Charge your platform fee separately, as you do now.
Milestone escrow your platform never has to hold
One server-side call per milestone. The client’s stablecoins lock in a contract until the work is accepted, then pay the freelancer. No account, no API key, nothing to the platform.
See the escrow API