Order Lifecycle
A complete walkthrough of the C2C order flow from ad creation to trade completion. Each state transition maps to specific API endpoints and error conditions.
State Flow
┌──────────────────────────────────────────────────────────────────────────────┐
│ Binance C2C Order Lifecycle │
└──────────────────────────────────────────────────────────────────────────────┘
[Ad Posted]
│
│ EP-7: update price EP-8: toggle online/offline
│ ───────────────── ────────────────────────────
▼
╔══════════╗
║ AD LIVE ║ ◀── EP-4 list your ads EP-0 searcher sees your ad
╚══════════╝
│
│ Buyer places order (EP-18, buyer side)
│ EP-11: checkIfCanPlaceOrder (buyer pre-check)
▼
╔══════════════════╗
║ PENDING_PAYMENT ║ ─── EP-13: get order detail
╚══════════════════╝ EP-15: list orders
│
│ Buyer marks as paid (EP-17, buyer side)
│ Chat: buyer sends payment proof (EP-34 retrieve, EP-31 mark read)
▼
╔═══════════════════╗
║ PAID / PROCESSING ║ ─── Verify payment in bank / payment app
╚═══════════════════╝ EP-12: checkIfCanReleaseCoin
│
│ Seller releases coin (EP-20, your side)
▼
╔══════════════╗
║ COMPLETE ║ ─── EP-16: appears in order history
╚══════════════╝
─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ Error Paths ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
PENDING_PAYMENT ─── Buyer cancels ──────────────────▶ [CANCELLED]
EP-10: checkIfAllowedCancelOrder
EP-9: cancelOrder (buyer side)
PAID ─────────────── Dispute raised ─────────────────▶ [IN APPEAL]
Binance mediation process
(no dedicated SAPI endpoint)
ANY STATE ──────────── EP-21: verifyAdditionalKyc ───▶ KYC gate
Buyer needs additional KYC
before trade can proceed
Step-by-Step Breakdown
Ad Posted
EP-5 (create) / EP-4 (list)Create your ad via Binance UI or EP-5 (requires 2FA). Once live, EP-4 lists your ads. Your bot uses EP-0 to see how your ad ranks against competitors.
# Check your ad ranking
sellers = await fetch_order_book("USDT", "USD", "BUY")
my_ads = await list_my_ads(client, api_key, api_secret)
for ad in my_ads:
rank = next((i for i, s in enumerate(sellers)
if s["advNo"] == ad["advNo"]), None)
print(f"Ad {ad['advNo']} rank: {rank}")Order Placed
EP-13, EP-15A buyer places an order against your ad. Your bot detects it via EP-15 (list orders) or via WebSocket events. Use EP-13 to fetch full order details including buyer info, fiat amount, and payment method.
surplusAmount decreases automatically. This is why sending surplusAmount in EP-7 price updates causes error 187049 — the cached value may already be stale.Payment Marked
EP-17 (buyer), EP-31, EP-34The buyer marks the order as paid (EP-17) and typically sends payment proof via chat. Your bot reads chat messages via EP-34 and marks them as read via EP-31. The order enters a payment verification window.
# Poll for new chat messages after order placed
messages = await get_chat_messages(client, api_key, api_secret, order_no)
for msg in messages:
if msg.get("msgType") == "image":
# Buyer sent payment proof image
print(f"Payment proof received: {msg['content']}")Coin Released
EP-12, EP-20After verifying payment, call EP-12 to confirm coin release is allowed, then EP-20 to release. Most P2P bots do this manually — release only after independently confirming payment in your bank or payment app.
Complete
EP-16The order reaches COMPLETE status. It appears in EP-16 (order history). Your ad'ssurplusAmount is permanently reduced. If the ad has been fully traded out, it goes offline automatically.
Error Scenarios and Recovery
Ad Live → Reprice
187049Cause: surplusAmount included in EP-7 body
Recovery: Remove surplusAmount. Send only advNo + price.
Ad Live → Reprice
187055Cause: Price outside allowed range for the currency
Recovery: Binary search within allowed range. EP-3 (getReferencePrice) can help find bounds.
Ad Live → Reprice
83229 / 83230Cause: Ad was taken offline by Binance risk system
Recovery: Mark ad as paused. Inspect via EP-2. Do not reprice until status is confirmed.
Any HMAC call
-1021 / -1022Cause: Clock drift — timestamp outside 1000ms window
Recovery: Sync time via /api/v3/time (UTIL-1), recompute offset, rebuild query and retry.
Order placed
EP-13 returns 401Cause: Adaptive auth: unsigned attempt rejected
Recovery: Retry with full HMAC signature. Expected behavior — not an error.
Order history
EP-16 returns -1104Cause: GET not accepted by this environment
Recovery: Retry as POST with identical params. Three-fallback chain handles this automatically.