Two-sided marketplace loop: consent gate → masked proxy → two-way SMS → Inbox → close
A two-sided marketplace contact flow is not one feature — it is five, run in sequence. A rider or buyer consents to be contacted for the transaction (or is on your suppression list and must not be contacted at all). The two parties get a bounded, number-hidden channel to coordinate. Some of those conversations turn into disputes a bot cannot settle, and those need a support agent. And when the trip or delivery finishes, the channel must die so neither party retains a way to reach the other. Each layer already has its own page — the decision tree across privacy primitives, the proxy session sequence, the raw webhook-thread loop, and the Inbox queue. This page is the composition: the order to run them in, what to carry between layers, and where the seams are. The one ordering rule that makes the rest of this page safe: consent gating and masking run in sequence, never as alternatives. A masked session hides who the parties are; it has no opinion on whether you may contact them. Suppression wins every time — a number your own consent controls suppressed stays suppressed even when the send goes out through a proxy.1. Gate consent before any proxy session
Check reachability before a session exists, not after traffic is flowing. The gate has three tenant-owned controls, each defaulting off until you enable it:- Run a DNC pre-flight scrub on the counterparty numbers when you onboard a batch of them — driver pools, seller lists, courier rosters.
POST /compliance/dnc/scrubreturns a per-number verdict (up to 500 per request), and both it and the single-number check require your organization’s Do-Not-Call opt-in, enabled in your compliance settings. Pull the flagged numbers from the roster before any of them enter a session. - Capture consent with the public consent form when contactability is claimed rather than transactional. The hosted page lets recipients opt in or out per channel, and its submissions write to the same consent surfaces the carrier-side
STOP/STARTkeywords write to — so an opt-out from either path suppresses the number at the send path, proxy sends included. - Keep these controls tenant-owned. You decide what to enforce and Orbit executes it; the platform does not impose a global gate on top of your list hygiene. The one exception is the federal TCPA dialing window on US voice calls — a platform-wide guard with no opt-out — which matters here because a marketplace loop that lets carriers call participants is subject to it like any other dial.
400 you return to your own caller, not a session you open and then regret. Gate first, mask second.
2. Create the proxy session per transaction
One session per order, ride, or delivery — never reuse a session across transactions, because a session is the pairing’s disposal boundary and reuse smears contact across unrelated orders.ttl_minutes from the table in Choosing a privacy primitive: 30–60 minutes for a ride-share pickup through drop-off, 60–120 for delivery with a gate/lobby buffer, up to 1440 (the max) for marketplace listings that stay live for days. A TTL longer than the transaction needs is not insurance — it leaves a finished pairing recontactable, which is the leak masking exists to prevent.
Store session_id, proxy_number, and expires_at on the order record. Show both parties the proxy number as the conversation’s address in your own UI, so no personal number leaks through your product surface either.
3. Run SMS and voice through the same proxy
There is nothing to route-configure. Once the session is active, both channels resolve on the proxy number:- SMS. Either participant texts the proxy number; Orbit matches the sender to the session and forwards to the other participant, sent from the proxy number.
- Voice. Either participant dials the proxy number; the call bridges to the counterparty. Ordering coordination is a voice habit — “meet me at the gate,” “I’m downstairs” — and it shares the same masked pairing. Neither side ever sees the other’s real number on either channel.
POST /api/v1/messages/sms addressed to the proxy number; Orbit resolves the session and delivers to the other participant, exactly like a handset’s own reply. The full field surface and the three cases that deliberately do not forward are in the proxy session guide.
4. Handle replies in your thread map — and escalate to the Inbox when a human enters
Most marketplace threads are coordination traffic your own service handles without a queue. The wiring is the webhook loop from Two-way SMS conversations: subscribe tomessage.received, receive the reply, key a thread map on the session’s address pair, and answer on the same addressing.
In a masked flow, the thread key is the session itself, not the participants — index the thread record by session_id from proxy.message.forwarded events, and your UI never needs to look up a participant to render or reply:
- A human must answer inside a live window — a dispute, a safety flag, a failed handoff. Create a ticket into the queue with
POST /api/v1/inbox/tickets/internal, carrying the order it belongs to and the session id as the thread reference, so the agent opens a thread the participants already live on. If the agent needs live conversation handling rather than ticket follow-up, the Inbox’s native inbound path opens the conversation on the queue instead — inbound keeps working on the same numbers, no re-wiring. - The conversation outgrows SMS or your team needs supervision — an agent sees the surfaced thread context, assignment, internal notes, and SLA clocks apply. Set up channels, routing rules, and SLA policies from the Inbox setup guide before the first escalation lands.
5. Close the session at completion
End the session when your app owns the completion event — trip finished, delivery confirmed, dispute resolved:closed, releases the proxy number to the pool, and stops all traffic resolution in one step. Handle these exactly as the taxonomy in Choosing a privacy primitive prescribes:
409 CONFLICTon close means already ended — closed by an earlier handler or expired by the sweeper. Treat it as success and never retry; the desired state already holds.- The TTL is the fallback, not the plan. If no completion handler runs, the session stops accepting traffic at
expires_atand the sweeper releases the number on its next pass. An expired session is a forgotten cleanup, not a leak. - Reconcile drift with the list call.
GET /api/v1/proxy/sessions?status=activetells you which sessions your order states think are finished but the proxy lifecycle thinks are open — close anything on that list.
- Gate consent; expect flagged numbers to fail at the gate before a session exists.
- Create the session; expect
proxy.session.created. - Text the proxy number from participant A; expect
proxy.message.forwardedand delivery to B. Repeat from B and from a voice dial. - Escalate one thread to the Inbox and answer from the queue, addressed to the proxy number.
- Delete the session; expect
proxy.session.closed, then confirm nothing forwards and neither party can be recontacted.
See also
- Choosing a privacy primitive — the decision layer: masking vs OTP vs channel primitives, plus the TTL table and the full error taxonomy this page defers to.
- Number masking: proxy privacy sessions — the proxy sequence in full: create, route, terminate, webhook verification.
- Two-way SMS conversations — the webhook thread map this page upgrades from.
- Inbox setup — channels, routing rules, macros, SLA, AI drafts for the queue escalations land in.
- DNC pre-flight scrub and public consent form — the tenant-owned consent gate from section 1.
- Proxy API reference — the field-level session endpoint surface.