Nearest-Partner Matching
How a paid booking is turned into the right partner on the right vehicle at the lowest cost that still meets the speed tier — eligibility, the bundling-first cascade, scoring, dispatch, fairness, and relay legs.
1. Overview — what matching decides
Matching is the engine that takes a paid booking and answers one question: which partner, on which vehicle, should carry this parcel — and by which strategy? The inputs are everything the booking already knows about the job, and the objective is deliberately cost-minimizing: among all assignments that satisfy the speed tier and the vehicle constraints, pick the cheapest to serve. Cheap-to-serve is how RideChain undercuts Porter and Bluedart on a 15 km interior route while still paying partners fairly.
Package shape
Size bucket (S/M/L/XL), weight bucket, category (documents, fertilizer, furniture, perishable…) and flags (fragile, perishable, cold, oversized). These set the eligible vehicle classes.
Geo & time
Pickup/drop coordinates, distance, and the delivery time window. Geolocation answers "who is near?"; the window decides whether a slow bundle is acceptable.
Speed tier & price
Urgent / Express / Bundled milk-run, and the fare the booker paid. The tier is the hard constraint; cost is what matching minimizes under it.
Trust tier
The partner's graduated trust level gates what they may be offered at all — new partners are limited to low-value, short, prepaid jobs (see Onboarding & KYC).
Matching does not work alone. It leans on Geolocation for nearest-partner queries (PostGIS + Redis GEOSEARCH), on Commission & Pricing for the rate card that makes one assignment cheaper than another, on trust tiers for eligibility, and on Fastest-Route Finding once a partner is chosen.
flowchart LR B["Paid booking
(size, weight, category,
geo, window, tier, price)"] --> F["Eligibility filter
vehicle class × trust tier"] F --> S["Cost-aware scoring
& ranking"] S --> O["Offer to best partner"] O --> A["Assigned + ETA"] G["📍 Geolocation
nearest queries"] -.-> F P["📊 Pricing / rate card"] -.-> S T["🛡️ Trust tiers"] -.-> F classDef src fill:#e8f0fb,stroke:#1f5fae,color:#143d6e; class G,P,T src;
2. Vehicle eligibility matrix
Before any scoring, matching narrows the partner pool to vehicles that can physically and legally carry the parcel. RideChain supports the full ladder of vehicles — cycle, bike, e-rickshaw, auto, Bolero, Tata Ace, tractor-trolley, mini-truck, tempo — and the eligible set is a function of (size bucket × weight bucket × category × distance). The booker may have overridden the recommendation at booking time; matching honours the chosen vehicle class as the floor.
| Size bucket | Weight bucket | Category | Distance | Eligible vehicle classes |
|---|---|---|---|---|
S | < 2 kg | Documents / small parcel | ≤ 6 km | Cycle · Bike |
S | < 2 kg | Documents / small parcel | 6–25 km | Bike · E-rickshaw |
M | 2–20 kg | General goods | ≤ 12 km | Bike · E-rickshaw · Auto |
M | 20–60 kg | Fertilizer / sacks | ≤ 15 km | Auto · Bolero · Tata Ace |
L | 60–150 kg | Furniture / bulky | any | Tata Ace · Mini-truck · Tempo |
L | 60–500 kg | Farm / agri-bulk | rural | Tractor-trolley · Mini-truck |
XL | > 150 kg | Heavy / long-haul | any | Mini-truck · Tempo · Tractor-trolley |
| any | any | Perishable / cold-chain | any | Fastest eligible + cold flag required |
| any | any | Fragile | any | Eligible set minus cycle (no padding); fragile handling |
cold flag set so a slow bundle is never chosen for it.Eligibility is also a trust gate and a document gate: motorized classes require a partner whose RC (VAHAN) and DL (Sarathi) are validated in Onboarding & KYC, and the per-class payout floor comes from the Commission & Pricing rate card. A vehicle the partner is not verified to operate is removed from the eligible set before scoring even begins.
flowchart TB J["Booking: size, weight,
category, distance, flags"] --> CAP{"Capacity fit?
weight & volume"} CAP -- "no" --> UP["Escalate to larger class"] CAP -- "yes" --> CAT{"Category constraints?"} CAT -- "perishable / cold" --> COLD["Require cold flag
+ fast vehicle only"] CAT -- "fragile" --> FR["Drop cycle, mark handling"] CAT -- "general" --> OK["Base eligible set"] UP --> DOC COLD --> DOC FR --> DOC OK --> DOC{"Partner verified
for this class? (VAHAN/Sarathi)"} DOC -- "no" --> RM["Remove partner"] DOC -- "yes" --> ELIG["Eligible partner × vehicle"]
3. The matching strategy — a cost-minimizing cascade
The single most important design choice is the order in which matching tries strategies. It is a cascade from cheapest-to-serve to most-expensive, falling through only when the cheaper option cannot meet the booking's constraints. This is what makes the bundled milk-run the default and on-demand the exception.
- Bundling-first (cheapest). Try to attach the parcel to an existing or planned route, or to a scheduled Block-Hub → village milk-run. Slightly slower, dramatically cheaper because partner time is shared across many parcels.
- On-demand (if urgent / no bundle). If the tier is urgent or no compatible bundle exists, dispatch the nearest eligible online partner directly — dedicated, faster, pricier.
- Reverse-auction / bid (heavy or long-haul). For large or long-distance jobs where a fixed rate is risky, broadcast to eligible heavy-vehicle partners and let them bid; the lowest acceptable bid wins.
- PUDO routing (via Points + hub). If no door-capable partner is sensible, route through RideChain Points and the Block Hub so the parcel rides shared infrastructure end to end.
flowchart TB START["Paid booking
eligible set ready"] --> URG{"Urgent tier?"} URG -- "no" --> BND{"Compatible bundle
or milk-run found?"} BND -- "yes" --> B1["① BUNDLING-FIRST
attach to milk-run
(cheapest, slower)"] BND -- "no" --> HVY URG -- "yes" --> HVY{"Heavy / long-haul?"} HVY -- "no" --> B2["② ON-DEMAND
nearest eligible online partner"] HVY -- "yes" --> B3["③ REVERSE-AUCTION
partners bid, lowest wins"] B2 -. "no door partner sensible" .-> B4 B1 --> DONE B2 --> DONE B3 --> DONE B4["④ PUDO ROUTING
via Points + Block Hub"] --> DONE["Assignment chosen"] classDef cheap fill:#e6f5ea,stroke:#2e8b57,color:#1d5a38; class B1 cheap;
4. The scoring function
Within whichever cascade branch is active, candidate partner/vehicle pairs are ranked by a single weighted score. Conceptually:
score = w₁·proximity(distance, ETA) + w₂·rating + w₃·acceptance_rate + w₄·vehicle_fit + w₅·trust_tier + w₆·fairness_rotation + w₇·green_bonus
| Term | What it measures | Why it lowers cost / risk |
|---|---|---|
| proximity (distance / ETA) | How close the partner is to pickup, by road ETA not crow-flies. | Less dead-mileage to reach pickup → cheaper, faster, less fuel. |
| rating | Rolling customer rating of the partner. | Fewer failed handovers, disputes and re-deliveries. |
| acceptance_rate | How often the partner accepts offers they receive. | Higher first-offer acceptance → fewer cascade hops, faster assignment. |
| vehicle_fit | How well the vehicle matches the job (not oversized). | A bike for documents beats a Bolero — right-sizing minimizes cost per parcel. |
| trust_tier | Graduated trust level from KYC + track record. | Gates eligibility for high-value/long jobs; reduces fraud/loss exposure. |
| fairness_rotation | Recent income/job share for this partner. | Spreads work, retains partners, prevents starvation (see §7). |
| green_bonus | Cycle / e-rickshaw / EV preference. | Lower-emission and often lower-cost legs for short urban hops. |
5. Dispatch flow & cascade
Once a booking is ESCROW_HELD / paid, the matching engine runs the dispatch loop: query nearby eligible partners, score and rank them, offer to the top candidate, and cascade to the next on decline or timeout. If the cascade exhausts every option, the booking moves to NO_PARTNER_FOUND and the held escrow is auto-refunded.
sequenceDiagram
autonumber
participant GW as "Go booking API"
participant MA as "Matching engine"
participant GEO as "PostGIS + Redis GEOSEARCH"
participant PT1 as "Partner (rank 1)"
participant PT2 as "Partner (rank 2)"
participant PAY as "Payments + escrow"
participant U as "Booker"
GW->>MA: "Booking PAID — dispatch"
MA->>GEO: "Nearest eligible partners (radius, vehicle class)"
GEO-->>MA: "Candidate list"
MA->>MA: "Score & rank (proximity, rating, fairness, fit)"
MA->>PT1: "Offer (auto-assign default OR accept within N s)"
alt Accepts in time
PT1-->>MA: "Accept"
MA-->>U: "Assigned partner + ETA"
else Declines or times out
PT1-->>MA: "Decline / timeout"
MA->>PT2: "Cascade: offer to next ranked"
PT2-->>MA: "Accept"
MA-->>U: "Assigned partner + ETA"
else Cascade exhausted
MA->>GEO: "Widen radius / check milk-run schedule"
GEO-->>MA: "Still none"
MA->>PAY: "NO_PARTNER_FOUND → auto-refund escrow"
PAY-->>U: "Full refund + notify"
end
NO_PARTNER_FOUND with an automatic full refund (see Payments).NO_PARTNER_FOUND → REFUNDED so the booker is made whole automatically. The platform holds no money, so the refund is a gateway reversal — see Payments & Integration.6. Bundling logic
Bundling is what makes the milk-run cheap. The engine decides two or more parcels can share a trip only when direction, capacity and time windows all line up, then batch-assigns the group to one partner or one scheduled milk-run.
| Compatibility check | Rule |
|---|---|
| Same direction | Pickups and drops fall along a shared corridor / toward the same village cluster — no large detours. |
| Capacity fits | Combined weight & volume stay within the chosen vehicle's class limit (the eligibility check, summed). |
| Time windows compatible | Every parcel's delivery window can still be met given the batched stop sequence; perishables tighten this. |
| Handling compatible | No conflicting flags (e.g. cold-chain not mixed with non-cold; fragile stacked safely). |
flowchart TB
P["New eligible parcel"] --> DIR{"Same direction as
an open bundle?"}
DIR -- "no" --> NEW["Seed new milk-run / on-demand"]
DIR -- "yes" --> CAP{"Capacity still fits?"}
CAP -- "no" --> NEW
CAP -- "yes" --> WIN{"Time windows
compatible?"}
WIN -- "no" --> SPLIT["Split batch / new bundle"]
WIN -- "yes" --> ADD["Add to bundle"]
ADD --> ASSIGN["Batch-assign to one
partner / milk-run"]
7. Fairness rotation
A pure cost-minimizer would funnel every job to the same handful of top-rated, perfectly-placed partners and starve everyone else. RideChain treats partner retention and rural livelihood as first-class goals, so fairness enters the score directly as the fairness_rotation term: a partner who has recently earned little or been offered few jobs gets a positive boost, nudging work their way when they are otherwise competitive.
flowchart LR RAW["Raw cost-aware rank"] --> ADJ["Apply fairness boost
(low recent income / few offers)"] ADJ --> CAP{"Anti-monopoly cap
reached for top partner?"} CAP -- "yes" --> NEXT["Prefer next eligible partner"] CAP -- "no" --> KEEP["Keep ranked order"] NEXT --> OUT["Final offer order"] KEEP --> OUT
8. Relay leg assignment
A relay (Mode B) booking decomposes into ordered legs — first-mile, hub-to-hub, last-mile. Matching treats each leg as its own job: every leg is independently filtered, scored and assigned to a partner whose coverage and vehicle suit that segment. A bike may win the first-mile pickup while a Tata Ace wins the hub-to-hub haul and an e-rickshaw the village last-mile.
flowchart LR S["Sender / origin Point"] -->|"leg 1 · first-mile"| PA["Partner A (matched for leg 1)"] PA -->|"handover at hub"| HUB["🏬 Block Hub
consolidate"] HUB -->|"leg 2 · matched independently"| PB["Partner B (matched for leg 2)"] PB -->|"leg 3 · last-mile, matched"| R["Receiver / dest Point"] classDef hub fill:#fff3e0,stroke:#f4920b,color:#8a5200; class HUB hub;
Because each leg is a separate assignment, each leg also carries a separate custody lock and a separate money slice. The custody chain across legs (assignment lock + parcel-QR + dual-OTP + geofence) is documented in Last-Mile Delivery, and the per-leg payout — released on that leg's handover — in Split-Money Settlement. If leg 2 cannot be matched immediately, the parcel simply waits in hub custody and is re-matched, without disturbing the already-completed leg 1.
9. Edge cases & failure modes
Matching assumes the unhappy path is routine: partners decline, go offline, GPS drifts, and the same job risks being offered to a crowd. Each risk below has a defined mitigation; the full catalogue lives in the Edge-Case Catalog.
| Scenario | Mitigation |
|---|---|
| No eligible partner online | Widen the search radius progressively → check the milk-run schedule for a later bundle → if still none, NO_PARTNER_FOUND and auto-refund the escrow. |
| All ranked partners decline | Cascade through the full ranked list; apply a surge bump to make the job more attractive and notify a wider pool before declaring no-partner. |
| Partner goes offline after accepting | Auto re-dispatch from MATCHING to the next candidate; apply a partner reliability penalty; booker not charged for the drop. |
| Stale GPS makes "nearest" wrong | Enforce a location-accuracy / freshness threshold; ignore stale fixes and recompute the nearest set on a fresh ping (see Geolocation). |
| Thundering herd — one job offered to many | Single-offer model with an assignment lock in Redis; only one partner holds the offer at a time, preventing duplicate accepts. |
| Over-capacity partner accepts too much | Capacity check at accept time (current load + this parcel vs vehicle limit); reject the accept and cascade if it would overflow. |
| Relay leg-2 unmatched at hub | Hold the parcel in hub custody, pause the leg SLA clock, and re-match leg 2 on a timer; leg 1 stays paid. |
| New partner offered a high-value job | Trust gate blocks it — new partners are eligible only for low-value, short, prepaid jobs until graduated (KYC tiers). |
| Bundling time-window conflict | If a parcel's window can no longer be met inside a batch, split the batch and re-seed a faster bundle or on-demand offer. |
| Fairness vs efficiency tradeoff | Fairness boost is bounded — it only reorders among already-eligible, near-tie candidates and never forces an unsafe or wildly-detoured assignment. |
| Vehicle mismatch discovered at pickup | Re-match to an eligible larger class (or upcharge per policy) rather than forcing an unsafe load; original offer voided. |
| Spoofed location to grab nearby jobs | Mock-location / fraud flag on the partner; offers suppressed pending review; cross-checked against the accuracy signals in Geolocation. |