Mask two parties’ numbers with a proxy session
A masking session lets two people text and call each other without either one ever seeing the other’s real phone number. This guide walks a session through its full life — create, route SMS and voice through the proxy number, terminate, and verify — and ends with a worked rideshare example you can replay against your own order states. The endpoint surface is field-level at Number Masking and the Proxy API reference; this guide is the sequence.1. What a masking session is
A session binds three numbers:- Party A — one participant’s real number, in E.164 (
+14155550100). - Party B — the other participant’s real number.
- The proxy number — an E.164 number Orbit draws from the shared number pool and holds for the session’s lifetime.
participant_a or participant_b — the only address either side can reach or see is the proxy number.
The lifecycle has three statuses:
An expired session is a session you forgot, not a leak: the sweeper runs on schedule and hands the number back. Close explicitly when your app knows the moment of completion — a trip finished, a delivery confirmed — so the number frees up right away and neither party can recontact the other past the point your product intends.
2. Create a session
Post the two participants and, optionally, how long the session may stay open:- Both participants are E.164 — leading
+, country code, digits, nothing else. A malformed number returns400naming the field. - The two participants must differ. Identical numbers return
400. ttl_minutesis the retention window: how long the proxy number stays bound to this pair. It defaults to 60, and accepts 1 up to 1440 (24 hours). Pick the longest window the two parties legitimately need to reach each other — a pickup, a dispatch, an appointment — not longer.- The proxy number comes from the shared pool, not from your organization’s one-time free trial number. There is no per-organization concurrency cap, so one session per active order is normal usage.
- The
expires_attimestamp is server-computed; store it if your product wants to warn participants before a session lapses.
3. Route SMS and voice through the proxy number
There is nothing to configure. The session routes on two checks, applied to every inbound SMS or call that lands on the proxy number:- The proxy number resolves to an
active, unexpired session. - The sender matches
participant_aorparticipant_b.
- A third number texts or calls the proxy number. The sender matches neither participant, so the traffic is dropped and nothing is delivered. Only the two bound numbers can ever use the session.
- The session is
closedorexpired. After termination the proxy number no longer resolves to anything, and inbound traffic to it goes nowhere. - The TTL has passed but the sweep hasn’t run yet. An active-looking row past its
expires_atstill rejects traffic — expiry is enforced on access, and the scheduled sweeper is the cleanup pass, not the gate.
4. Terminate the session and redact the pairing
End a session the moment your product knows the exchange is over:closed, the proxy number is released back to the pool for reassignment, and inbound traffic to that number stops resolving. The redaction is complete at close — neither party can reach the other afterward, and the number that linked them no longer names either of them. Keep the session’s proxy number in your conversation view after close, since it remains the only address either participant ever saw; pair it with message history redaction if your data policy also covers stored bodies.
If you close nothing, the TTL is your retention bound: at expires_at the session stops accepting traffic, and the sweeper marks it expired and releases the number in the same pass.
Edge rules worth encoding in your completion handler:
- Only an
activesession closes. ADELETEon an alreadyclosedorexpiredsession returns409— treat it as “already ended,” not as a failure to retry. - If your completion event fires near the TTL boundary, the sweeper may win the race and the close comes back
409. The outcome is identical — the number is released either way — so treat a409on close as a successful end state. - Reconcile with
GET /api/v1/proxy/sessions?status=active: if your app believes an order is finished but a session still readsactive, close it. That list call is the drift check between your order states and the proxy lifecycle.
5. Verify the flow from webhook events
Subscribe a webhook endpoint to the three proxy events and you can verify every step above without polling — see the webhook consumer guide for endpoint registration and signature verification. The events arrive in this order during a healthy session: 1.proxy.session.created — the session exists and its number is bound.
proxy.message.forwarded — one per forwarded message, telling you the direction:
proxy.session.closed — the pairing is released:
- Create a session between two test numbers you control. Expect
proxy.session.createdand assert theproxy_numberis not either participant. - Text the proxy number from the participant A handset. Expect
proxy.message.forwardedwithfrom_participantequal to A andto_participantequal to B, and the message arriving on B from the proxy number. - Reply from B. Expect the webhook with the direction reversed — confirmation the session is symmetric.
- Text the proxy number from a third number. Expect silence: no forward, no webhook.
- Delete the session. Expect
proxy.session.closed, then text the proxy number once more from A — nothing should forward.
Worked example: a delivery from dispatch to handoff
A courier app masks the courier–customer channel for one delivery. Dispatch assigns a courier. The app creates a session with a 90-minute TTL — dispatch through handoff, plus a buffer for gate and lobby calls:proxy.session.created arrives; the app shows both sides the proxy number as the contact for this delivery.
The customer texts the proxy number asking the courier to leave the package at the side door. The inbound resolves to the session, the sender matches party B, and the text reaches the courier from the proxy number — proxy.message.forwarded records the direction for the conversation view. The courier calls back the same number and the voice leg bridges to the customer.
Handoff confirms. The app deletes the session. proxy.session.closed arrives, the number goes back to the pool, and neither side can recontact the other. If the confirmation event is ever lost, the 90-minute TTL closes the session on schedule through the sweeper instead — slower, same end state.
See Also
- Number Masking — field-level reference for the session endpoints, validation rules, and TTL-by-use-case table.
- Proxy API reference — the endpoint surface.
- Webhook consumer guide — endpoint registration, signature verification, and delivery semantics.
- Webhook events reference — the full event catalog.
- Buy and provision numbers — when you need dedicated DIDs rather than shared-pool proxy numbers.