Skip to main content
The Escrows API allows you to create and manage secure escrow transactions between buyers and sellers.

Overview

Escrows are secure transactions where funds are held until both parties agree that the transaction is complete. The API provides endpoints to:
  • Create new escrow transactions
  • List and filter your escrows
  • Get detailed information about specific escrows
  • Update, deliver, and cancel pending escrows
  • Get contract details and PDF URLs
  • Receive real-time notifications via webhooks
For critical actions that require user interaction (signing contracts, paying, completing, disputes, and cancellation flows), use Checkout Sessions to redirect users to dhmad.tn.
Your developer account must be associated with a user account to create escrows (unless you provide a sellerEmail). The associated user account must have email verification completed. Phone verification is only required when the buyer accepts and pays, not at escrow creation.

Escrow Modes

When creating an escrow, set the mode field to choose the transaction flow. All modes share the same statuses (pendingpaid → …) but differ in whether a contract is used and how delivery completes.

standard

Full flow with a contract both parties sign before payment. Best for services and freelance work between two users.

quick

No contract; buyer pays immediately, then releases funds via complete. Seller deliver is optional. Best for physical or digital products between two users.

instant

No contract; you are the seller (omit sellerEmail). Buyer pays in one step; calling deliver auto-completes and releases funds. Best for in-app purchases and digital goods.

Standard mode

  • Set mode: "standard" (default) with contractTerms.
  • A contract is automatically created; both parties must sign before the buyer can pay.
  • Checkout actions: sign_contractaccept_paycomplete. Seller may call Deliver Escrow first, but it is optional — the buyer can complete directly from paid (early release) if they are already satisfied.
  • Provide sellerEmail when the seller is a third party, or omit it to use your associated user as seller.
  • Optional feeBearer ("seller" default, or "buyer") controls who pays platform and developer fees — see Create Escrow.
State flow: pendingpaid → (delivered, optional) → completed. Buyer complete is valid from paid or delivered.

Quick mode

  • Set mode: "quick". Do not send contractTerms or autoSign.
  • No contract is created; the response includes contract: null and escrow.mode: "quick".
  • Checkout actions: accept_paycomplete. Seller may call Deliver Escrow first, but it is optional — the buyer can complete directly from paid without waiting for deliver. The sign_contract action returns 400 (QUICK_ESCROW_NO_CONTRACT).
  • Use when escrows are between two other parties (sellerEmail required) and you want a faster checkout without a contract — e.g. product sales, digital downloads with buyer confirmation.
  • Minimum internal ledger value (amountTnd) is 5 TND after FX lock (same as standard).
State flow: pendingpaid → (delivered, optional) → completed. Buyer complete is valid from paid or delivered.

Quick mode with purchase proof

Add fulfillmentPolicy when you need the seller to prove purchase or shipment before delivery is unlocked — e.g. marketplaces, crowd-shipping, or product-deal apps.
  • Set mode: "quick" and fulfillmentPolicy.type: "purchase_proof_required".
  • After the buyer pays, escrow status stays paid while proofInfo.phase tracks the proof gate (waiting_seller_proof → review → accepted).
  • Checkout actions: accept_paysubmit_proof (seller) → review_proof (buyer) → complete. Seller deliver is optional after proof is accepted; buyer can complete from paid without deliver.
  • Fetch proof files with GET /escrows/:id/proof; handle escrow.proof.* webhooks.
See the Quick Escrow with Proof guide for the full walkthrough.

Progressive release (hourly / recurring contracts)

Add progressiveRelease when the total is known up front but the work arrives incrementally — hourly contracts, retainers, or any arrangement billed per period.
  • Include a progressiveRelease object on creation (presence is the opt-in; do not send enabled), optionally with autoApproveHours (the client’s review window). Composes with standard or quick mode; not available for instant.
  • The escrow stays paid across many payouts. Your backend claims each billing period with Create Payout Request — send face amount + currency or ledger amountTnd. You may do this on the seller’s behalf, so the freelancer takes no weekly action.
  • GET and balance webhooks expose face + TND progressive balances (availableForRelease / reserved / releasedTotal and *Tnd counterparts) plus stable fundedAmount* — do not invent face amounts with tnd / fxRateUsed. On terminal status, float fields are forced to 0.
  • The client approves (approve_release), rejects (reject_release, which opens a dispute for that period only), or does nothing — in which case the period releases automatically at its deadline.
  • Top up the same escrow with add_funds; a zero balance emits escrow.balance.exhausted and leaves the escrow open.
  • End the contract with Close Escrow, which settles owed periods and refunds the unused balance.
State flow: pendingpaid → (many partial releases, balance decreasing) → completed on close. See the Progressive Release guide for the full walkthrough.

Instant mode

For digital goods (e.g. in-app coins, AI credits), the full standard flow has too many steps and can hurt conversion. Use instant when you are the seller:
  • Set mode: "instant" and omit sellerEmail (your associated user is the seller). Do not send contractTerms or autoSign.
  • No contract is created; the response includes contract: null and escrow.mode: "instant".
  • Checkout action: accept_pay only. The actions sign_contract and complete return 400.
  • When you receive the escrow.status.updated webhook (pendingpaid), deliver the digital goods (e.g. credit the user’s balance).
  • Call Deliver Escrow (POST /api/v1/escrows/:id/deliver). The escrow auto-completes and funds are released immediately (no buyer confirmation step).
  • Minimum internal ledger value (amountTnd) is 0.01 TND after FX lock.
  • Guest checkout (buyer pays without a DHMAD account) is supported for instant escrows via accept_pay.
  • feeBearer can be set at create time; it cannot be changed via update after creation for instant escrows.
State flow: pendingpaidcompleted (the delivered state is skipped; calling deliver transitions directly to completed)

Mode comparison

estimatedDeliveryDays is a legacy optional field. Omit it for new integrations — it is not used by platform enforcement.

Escrow Statuses

Escrows progress through the following statuses:

pending

Escrow created, waiting for payment. The buyer needs to accept and pay.

paid

Payment received and locked in escrow. The buyer can release funds via a complete checkout session (early release), or the seller can call deliver first (optional). A progressive-release escrow stays here across many partial payouts.

delivered

Service/product delivered. Waiting for buyer to confirm completion. Skipped in instant mode.

completed

Transaction completed successfully. Funds released to seller.

cancelled

Escrow was cancelled. Funds returned to buyer if payment was made.

Buyer Handling

When creating an escrow, you provide the buyer’s email address. The system handles two scenarios:

Existing User

If the buyer email corresponds to an existing user account:
  • The escrow is linked to their account
  • They receive an in-app notification
  • They receive an email notification
  • They can view and interact with the escrow immediately

New User

If the buyer email doesn’t correspond to an existing user:
  • An invitation email is sent with a registration link
  • The escrow is created with the email stored
  • Once they register using the link, the escrow is automatically linked to their account
  • The registration link includes a secure token for verification

API Endpoints (Direct)

These endpoints can be called directly from your server using your API key:

Checkout Sessions (Redirect)

For actions that require user interaction, use Checkout Sessions to redirect users to dhmad.tn:
  • Sign Contract — User signs the escrow contract (standard mode only)
  • Accept & Pay — Buyer reviews and pays for the escrow (all modes)
  • Submit proof — Seller uploads purchase/shipment proof (Quick Escrow with Proof only)
  • Review proof — Buyer accepts proof or requests corrections (proof escrows only)
  • Complete — Buyer releases funds (standard and quick modes; not instant). Valid when escrow is paid or delivered; deliver is not required first.
  • Dispute — User opens a dispute
  • Request cancellation — Buyer requests cancellation (paid or delivered; buyer email only)
  • Cancel escrow — Seller cancels immediately (paid or delivered; seller email only)
  • Reject / accept cancellation — Seller responds to a pending buyer cancellation request (seller email)
  • Approve release — Client approves a payout request, releasing that period now (progressive release only; buyer email, metadata.requestId required)
  • Reject release — Client rejects a payout request, opening a dispute for that period only (progressive release only; buyer email, metadata.requestId required)

Escrow amounts support face currencies TND, USD, and EUR via the currency field. The platform ledger uses TND only: amountTnd is locked at creation and used for fees and payouts. See Create Escrow for details.