Skip to content

Runbook: Escrow Dispute Arbitration ​


1. Context & Incident Classification ​

Dispute TypeCommon CausesInvestigation SLAArbitration Authority
Non-Delivery / Hostage PINVendor claims delivery; buyer denies receiving item or refuses PIN handover.$< 12\text{ hours}$Tier 1 Dispute Specialist
Defective / Damaged ItemItem arrived broken, counterfeit, or with wrong specifications.$< 24\text{ hours}$Tier 2 Support Lead
High-Value / Suspected FraudTransaction $> ₦100,000$, fake courier tracking, or off-platform coercion detected by ChatGuard.$< 4\text{ hours}$Senior Arbiter + Maker-Checker Review

2. Evidence Gathering Protocol ​

Before executing any fund disposition, arbiters must collect and log evidence across four independent telemetry channels:

mermaid
graph TD
    subgraph Evidence Ingestion Channels
        CHAT[1. ChatGuard & In-App Transcripts]
        PIN[2. Delivery PIN & Verification History]
        LOGISTICS[3. Campus Hub / Courier Dispatch Waybill]
        MEDIA[4. Buyer Unboxing Photo / Defect Evidence]
    end

    subgraph Forensic Synthesis
        ARBITER[Dispute Arbiter Case Review]
    end

    CHAT --> ARBITER
    PIN --> ARBITER
    LOGISTICS --> ARBITER
    MEDIA --> ARBITER

    ARBITER --> DECISION{Decision Matrix}
    DECISION -->|Vendor at Fault| REFUND[100% Buyer Refund via ReviewedWalletRefundService]
    DECISION -->|Buyer Unresponsive / Fraud| RELEASE[100% Vendor Release via OrderService]
    DECISION -->|Mutual Compromise| SPLIT[Split Settlement / Partial Credit]

Channel 1: In-App Chat Transcript & ChatGuard Logs ​

Query chat history between the buyer and vendor:

sql
SELECT 
    cm.id, cm.sender_id, cm.message, cm.created_at,
    cs.flagged, cs.reasons
FROM public.chat_messages cm
LEFT JOIN public.chat_scan_results cs ON cs.message_id = cm.id
WHERE cm.conversation_id = '<CONVERSATION_ID>'
ORDER BY cm.created_at ASC;

Look for: Attempts by either party to move off-platform, abusive language, or admission of item handover/receipt.

Channel 2: Delivery PIN Forensics ​

Inspect the PIN state in public.order_verification:

sql
SELECT 
    ov.order_id,
    ov.delivery_code,
    o.status,
    o.payment_status,
    o.created_at,
    o.updated_at
FROM public.order_verification ov
JOIN public.orders o ON o.id = ov.order_id
WHERE o.id = '<ORDER_ID>';

Critical Forensic Invariant: If the vendor successfully entered the correct 4-digit PIN prior to dispute filing, the burden of proof shifts heavily to the buyer to demonstrate non-receipt (since the PIN is exclusively displayed inside the buyer's authenticated account).

Channel 3: Campus Hub Logistics Verification ​

  • If fulfilled via Campus Hub: Contact the designated Campus Hub Representative to confirm if the package was physically logged into the hub locker and whether a student courier was dispatched.
  • If fulfilled via Third-Party Courier: Verify external waybill tracking on GIG Logistics, Kwik, or Speedaf portals.

3. Arbitration Decision Matrix ​

ScenarioSubstantiated EvidenceFinal DispositionSystem Execution
Vendor Non-FulfillmentNo courier tracking after 48h; vendor uncontactable in chat.100% Refund to BuyerIssue full wallet refund; apply Strike 1 to vendor.
Damaged / Incorrect ItemBuyer photo verified vs product listing; return case opened.Return & Full RefundDispatch campus runner via AtomicReturnCaseService; refund executed upon vendor restock inspection.
Buyer False ClaimVendor entered correct PIN; courier provides GPS timestamp at buyer hostel.100% Release to VendorDismiss dispute; trigger process_order_fund_release().
Partial Defect / CompromiseItem functionally intact but missing non-essential accessory.Split Settlement20% partial refund to buyer wallet; 80% released to vendor.

4. Execution via Command Services ​

All dispute fund adjustments are executed through audit-logged command services enforcing Maker-Checker rules:

Pathway A: Executing Buyer Refund (ReviewedWalletRefundService) ​

For full or partial refunds back to the buyer's wallet:

typescript
await ReviewedWalletRefundService.propose({
  orderId: targetOrderId,
  buyerId: targetBuyerId,
  amountMinor: refundAmountMinor,
  reason: "Dispute Arbitrated in Buyer Favor: Item defective (Case #" + disputeCaseId + ")",
  actorId: arbiterStaffId
});

Note: For refunds exceeding ₦50,000, a second senior support lead must review and execute the approval in the admin portal.

Pathway B: Executing Vendor Escrow Release (OrderService) ​

If the dispute is ruled in favor of the vendor:

typescript
await OrderService.forceReleaseEscrow(
  targetOrderId,
  arbiterStaffId,
  "Dispute Arbitrated in Vendor Favor: Proof of delivery verified via campus hub receipt"
);

5. Post-Arbitration Actions & Strike Enforcement ​

  1. Vendor Strike Allocation:
    • If ruled against the vendor for intentional fraud, counterfeit goods, or failure to fulfill, log a formal Strike via VendorService:
    sql
    UPDATE public.vendor_profiles 
    SET strike_count = strike_count + 1,
        last_strike_at = now(),
        strike_reason = 'Dispute Case #<DISPUTE_ID>: Failed fulfillment'
    WHERE id = '<VENDOR_ID>';
  2. Buyer Fraud Monitoring:
    • If a buyer exhibits a pattern of filing false disputes on verified deliveries ($> 2$ false claims), set profiles.buying_disabled = true pending KYC re-verification.
  3. Automated Notification Dispatch:
    • System dispatches itemized arbitration ruling emails to both buyer and vendor with legal rationale.

Released under Proprietary Enterprise License.