Escrow API
This page documents the live public escrow surface served under
https://api.gopiaxis.com/api.
Overview
Public escrow routes:
POST /api/escrows/GET /api/escrows/{escrow_id}GET /api/escrows/{escrow_id}/statusPOST /api/escrows/{escrow_id}/terms/{term_id}/fulfillPOST /api/escrows/{escrow_id}/releasePOST /api/escrows/{escrow_id}/reversePOST /api/escrows/{escrow_id}/disputes
Public Contract Notes
The guaranteed create-escrow request schema is:
receiver_id(optional; defaults to the authenticated merchant account)amountcurrency_codepayment_methodtermsexternal_user_iduser_infouser_location
This differs intentionally from direct payments:
direct payment uses
currencyandrecipient_idescrow uses
currency_codeandreceiver_iddirect payment credits the authenticated merchant collector by default
public escrow collection also defaults the release destination to the authenticated merchant
if
receiver_idis supplied, it must match the authenticated merchantAccount.idreceiver_idmust be a PiaxisAccount.id; do not send aMerchantProfile.idorUserProfile.id
Some long-form examples may include richer descriptive keys inside term payloads. That is acceptable as term-specific data, but the guaranteed outer request contract is the field list above.
Authentication
Merchant-controlled calls use:
api-key: YOUR_MERCHANT_API_KEY
Content-Type: application/json
X-piaxis-Client-ID: YOUR_MERCHANT_CLIENT_ID
For piaxis_external sender flows, use OAuth bearer auth where the route
requires user-context authorization.
For unregistered external-money flows, request an OTP first with
POST /api/request-otp and carry it in user_info.otp.
Create Escrow
- POST /api/escrows/
Create a new escrow.
Canonical MTN escrow example
POST /api/escrows/ HTTP/1.1
Host: api.gopiaxis.com
api-key: YOUR_API_KEY
Content-Type: application/json
X-piaxis-Client-ID: YOUR_MERCHANT_CLIENT_ID
{
"amount": "50000.00",
"currency_code": "UGX",
"payment_method": "mtn",
"terms": [
{
"type": "delivery_confirmation",
"data": {
"deadline": "2026-12-31T23:59:59Z"
}
}
],
"user_info": {
"email": "[email protected]",
"phone_number": "+256700000000",
"otp": "123456"
}
}
Receiver semantics
Public escrow collection infers the release destination from the authenticated merchant API key.
omitting
receiver_iduses the authenticated merchantAccount.idautomaticallyif
receiver_idis supplied, it must still match the authenticated merchantAccount.idthis public collection route does not use
receiver_idto choose some other merchant or platform accountif you want to hold funds through escrow and later pay an internal or external beneficiary, release into the authenticated merchant wallet first and then create a disbursement
Marketplace sequence
For a platform-managed order or service flow:
authenticate as the merchant or platform account that should collect and create the escrow
the customer pays into escrow
release the escrow to that same authenticated merchant or platform account when the order or service is complete
if the final beneficiary should be paid on MTN or Airtel mobile money, create a separate direct disbursement to the beneficiary phone number
This keeps the roles explicit:
the authenticated merchant receives the escrow release on this public collection route
recipients[].recipient_idis only for later internal wallet payoutsexternal mobile-money payees are handled later through the disbursement routes with
phone_number
Choose the right flow
Use direct payment when the authenticated merchant should collect immediately and no later escrow fulfillment is needed.
Use public escrow collection when the authenticated merchant should still be the final collector, but release should wait for payer and merchant actions such as delivery confirmation or meeting verification.
Use direct disbursement when you already control the collected balance and want to pay a beneficiary immediately.
Use escrow disbursement when the payout beneficiary, not the collecting merchant, must satisfy terms before payout. In escrow-disbursement batches the merchant or platform is the escrow sender and the beneficiary becomes the escrow receiver.
If your rule is collect from buyer B now, pay user C later, keep the collection on POST /api/escrows/ with the merchant as receiver, release into the merchant wallet, and then create a direct disbursement or escrow disbursement for user C.
Canonical piaxis_external escrow example
POST /api/escrows/ HTTP/1.1
Host: api.gopiaxis.com
api-key: YOUR_API_KEY
Authorization: Bearer YOUR_ACCESS_TOKEN
Content-Type: application/json
{
"amount": "75000.00",
"currency_code": "UGX",
"payment_method": "piaxis_external",
"external_user_id": "platform_user_123",
"terms": [
{
"type": "delivery_confirmation",
"data": {
"deadline": "2026-12-31T23:59:59Z"
}
}
]
}
Guaranteed create response shape
{
"id": "7680cfa9-b0cf-43a7-974b-108b89bd5ebe",
"amount": "50000.00",
"currency_code": "UGX",
"status": "pending",
"payment_method": "mtn",
"sender_id": null,
"receiver_id": "096b723a-45c5-4957-94d7-747835136265",
"created_at": "2026-01-15T10:30:00Z",
"updated_at": "2026-01-15T10:30:00Z",
"terms": [],
"products": [],
"location": null,
"required_actions": [],
"external_user_id": null
}
Read Escrow
- GET /api/escrows/{escrow_id}
Fetch the escrow record in the canonical typed response shape.
- GET /api/escrows/{escrow_id}/status
Fetch an operational status view with buyer info, term statuses, and
all_terms_met.
The two routes are both public and intentionally return different detail shapes.
Fulfill Terms
- POST /api/escrows/{escrow_id}/terms/{term_id}/fulfill
Fulfill a single escrow term.
Role model on this public collection route
For escrows created by POST /api/escrows/ the protected actors are:
payer or customer = escrow sender
authenticated merchant collector = escrow receiver
That means:
buyer-side confirmations must come from the original payer
seller-side confirmations must come from the authenticated merchant whose account matches
receiver_idif you want a downstream seller, driver, or beneficiary to become the protected receiver instead, use
POST /api/escrow-disbursementsrather than trying to route collection escrow directly to that user
Inspect required actions first
Use GET /api/escrows/{escrow_id} and inspect required_actions before building a fulfill UI.
Typical action hints returned by the live code include:
delivery_confirmation_requiredwithconfirmation_methoddelivery_meeting_requiredwithmax_distance_kmagreement_requiredwithagreement_optionsand current statusproof_requiredwithproof_typeand optionaldeadline
Term-specific payloads
The outer request shape stays the same, but data keys vary by term_type. The live handler branches on those keys, so send the term-specific payload instead of a generic confirmed flag.
Delivery confirmation
Send confirmation_method plus either buyer_device_info or seller_device_info.
Buyer confirmation example:
{
"term_id": "4215567e-6928-4a9d-85d8-c3b1708bdcec",
"term_type": "delivery_confirmation",
"data": {
"confirmation_method": "signature",
"buyer_device_info": {
"device_id": "buyer-device-001",
"platform": "android",
"app_version": "1.0.0"
}
},
"user_info": {
"email": "[email protected]",
"phone_number": "+256700000000",
"otp": "123456"
}
}
Seller confirmation example:
{
"term_id": "4215567e-6928-4a9d-85d8-c3b1708bdcec",
"term_type": "delivery_confirmation",
"data": {
"confirmation_method": "signature",
"seller_device_info": {
"device_id": "merchant-ops-01",
"platform": "web"
}
}
}
Use user_info for an unregistered external payer. Use bearer auth for registered piaxis or piaxis_external payers. Use the authenticated merchant API key for the seller-side confirmation.
Meeting delivery
Buyer confirmation sends buyer_latitude and buyer_longitude. Seller confirmation sends seller_latitude and seller_longitude. Both sides may include device_info. The route records caller IP automatically and only completes the term after both sides submit coordinates within the allowed proximity.
Buyer meeting example:
{
"term_id": "4215567e-6928-4a9d-85d8-c3b1708bdcec",
"term_type": "meeting_delivery",
"data": {
"buyer_latitude": 0.3476,
"buyer_longitude": 32.5825,
"device_info": {
"device_id": "buyer-phone-01",
"platform": "android"
}
},
"user_info": {
"email": "[email protected]",
"phone_number": "+256700000000",
"otp": "123456"
}
}
Seller meeting example:
{
"term_id": "4215567e-6928-4a9d-85d8-c3b1708bdcec",
"term_type": "meeting_delivery",
"data": {
"seller_latitude": 0.3477,
"seller_longitude": 32.5826,
"device_info": {
"platform": "web"
}
}
}
Other supported term types
agreement: sendagreement_typeasrelease,reverse, orsplit; when usingsplit, also sendsplit_percentage. This flow currently requires a registered account actor.location: sendlatitudeandlongitude.meeting: sendlatitudeandlongitude.password: sendpassword.proof: submitfilesfirst; later sendverifyastrueafter the proof is ready to approve.custom: send the term-specificdataexpected by your custom rule.return_period: sendreturn_requestedwhen the buyer is requesting a return within the allowed window.
Guaranteed request fields:
term_idterm_typedatauser_info
Delivery confirmation and meeting-delivery responses include requires_other_party until both sides have submitted their part. Other term types may return pending or release the funds immediately once all terms are met.
Release Escrow
- POST /api/escrows/{escrow_id}/release
Guaranteed request fields:
verification_codeverification_methoduser_infoforcereason
Example
{
"force": true,
"reason": "Buyer requested immediate release",
"user_info": {
"email": "[email protected]",
"phone_number": "+256700000000",
"otp": "123456"
}
}
Reverse Escrow
- POST /api/escrows/{escrow_id}/reverse
Guaranteed request fields:
reasonverification_codeverification_methoduser_info
Disputes
- POST /api/escrows/{escrow_id}/disputes
Guaranteed request fields:
reasoninitiator_roleuser_info
Implementation Notes
Persist the returned
escrow_idimmediately.Reconcile state from both polling and webhooks.
Model term fulfillment as idempotent on your side.
Authenticate collections as the merchant or platform account that should collect released funds.
Use disbursement recipients, not
receiver_id, when you need to route money to a different internal or external beneficiary later.