Continuous monitoring: SIM-swap and roaming subscriptions
The one-shot checks in Network signals before you send answer “is this number suspicious right now?” at the moment you ask. A monitoring subscription answers the follow-up: “tell me the moment it changes.” You enroll a number once, and the operator pushes a CloudEvent to your webhook when the SIM swaps, or when the device starts or stops roaming. No polling loop, no gap between checks. Orbit exposes two subscription families under/api/v1/numbers/network-apis, both aligned with the CAMARA event-subscription contract:
Each family also has
GET and DELETE routes on /subscriptions/{subscriptionId} to read state and tear down. Every request carries your X-API-Key.
Check vs subscription: when to enroll
Polling the one-shot checks is the right shape for a batch screen: you already hold a recipient list, one request per recipient, and you cache the verdict for the send window. A subscription is the right shape when a number matters continuously, not only at the moment you send or log someone in:- High-value accounts. Accounts whose takeover would be catastrophic (large wallets, payout rails, admin roles) deserve monitoring that reacts in seconds, not at the next poll.
- Step-up windows. After a password reset, a new-device login, or a suspicious
risk:scoreverdict, enroll the number for the risk window instead of pollingsim-swap:checkevery few minutes until the window closes. - Home-network fleets. IoT devices and domestic-only customer bases where a device answering abroad is itself the alert. That is the roaming subscription’s native use case.
subscriptionExpireTime— the subscription auto-expires at the RFC-3339 timestamp you set. Fit it to your risk window.subscriptionMaxEvents— the operator stops after delivering that many events (1 to 1000). Useful as a safety cap.DELETE— you tear the subscription down explicitly. Omit both limits and this is the only way it ends.
Enroll a number
The enrollment body is the same shape for both families: the number, your webhook, and optional limits.
A successful enrollment returns
201 with the operator-issued subscription id, its lifecycle status, and its expiry. Store the id; the read and teardown routes take it as a path parameter.
- Send an
Idempotency-Key. If your enroll call retries on a network error, the key prevents a second live subscription for the same number. notificationUrlmust be HTTPS. Plain-HTTP and internal addresses are rejected with a422.- The roaming variant is the same call with a different path.
POST /device-roaming-status/subscriptionstakes the identical body; the operator then deliversroaming-on/roaming-offevents instead ofswapped.
Read state and tear down
Read a subscription’s current state by id:id / status / startsAt / expiresAt shape, so a reconciliation job can compare what the operator reports against what you enrolled.
Tear down with DELETE; a 204 means the operator accepted the teardown and stops delivering notifications.
204. Your consumer must ack and discard events for subscriptions it no longer tracks, not error on them. The same tolerance covers subscriptions that hit subscriptionExpireTime or subscriptionMaxEvents a beat before your bookkeeping does.
Consume the CloudEvents
The operator POSTs each event to yournotificationUrl as a CloudEvent. Three rules make the receiver production-grade; they are the same rules as the production webhook consumer checklist, adapted to this sink:
- Authenticate the callback. On this sink the operator presents your
notificationAuthTokenas anAuthorization: Bearerheader on every delivery. Reject anything else with a401. (Orbit-dispatched webhooks elsewhere in the platform authenticate differently, with theX-Orbit-SignatureHMAC; the pattern below swaps only the authenticity check.) - Dedupe on the event
id. Delivery is at-least-once; the same event can arrive more than once. Persist seen ids in your database, not in memory, so dedupe survives restarts. - Ack fast. Verify, dedupe, enqueue, return
200. The decision logic runs in a queue worker, never inline in the handler.
type, resolve the number from the event’s data block (its exact shape follows your operator’s CAMARA implementation; the device.phoneNumber layout shown here matches what Orbit enrolls), and apply your own policy:
sim-swap:check in your login flow as well; the subscription catches swaps between logins, the check catches them at the door.
Roaming on and off: pair before you act
The roaming family delivers two event types,roaming-on when the device starts roaming and roaming-off when it stops. Treat them as two edges of one state, not as two independent alarms.
Devices near a border, or under roaming agreements that re-anchor periodically, can flap: roaming-on, roaming-off, roaming-on inside an hour. Acting on a single roaming-on (freezing an account, holding a payout) turns border towns into false positives. Debounce instead:
- Persist the latest event type and timestamp per number when the worker processes an event; do not act inline.
- A scheduled pass evaluates only numbers whose state has been unchanged for your quiet window (30 to 60 minutes is a workable default for a domestic-only customer base).
- A
roaming-offthat follows aroaming-oninside the quiet window resolves it: the device came home, and the pair is a log line, not an alert. - A
roaming-onthat survives the quiet window is your corroborating signal for the fraud review queue, alongside SIM-swap and device-swap verdicts.
Coverage and fail-soft
Subscriptions exist only when a GSMA/CAMARA operator is configured for the deployment. ProbeGET /api/v1/numbers/network-apis/status before you build on this surface:
{ "enabled": false } every enrollment returns 503 SERVICE_UNAVAILABLE, and your integration must route around the whole family: keep the polling path from Network signals before you send (sim-swap:check, device-roaming-status:retrieve) as your standing screen. It is also the right fallback inside an enabled deployment for the highest-value numbers, because absence of events is not evidence of absence of change: a delivery gap means nothing the operator could see happened, nothing more. Periodic one-shot checks complement subscriptions; they do not compete with them.
Tenant controls
Concretely: pair enrollment with your existing consent flows (see Consent management), and keep the act decisions, revoke, step-up, hold, refuse, in your own policy code with your own thresholds. Enrollment never sends, routes, or blocks a message; outbound messaging stays on your normal send path. See Country compliance requirements for the tenant-owned controls these signals feed into.In the console
The same subscriptions are operable from the console under Developer → Network APIs. The SIM Swap monitoring card enrolls a number, shows the subscriptions created from the console with their operator status (Active, Inactive, or Pending), re-reads their state, and stops them behind a confirmation dialog; the matching card below it does the same for roaming monitoring. Use the console for manual operations and debugging; your production automation enrolls and tears down through the API above.What’s next
- Network signals before you send — the one-shot checks these subscriptions complement.
- Production webhook consumer checklist — the full receiver pattern: retry ladder, dead-letter replay, and the 7-day dedupe window.
- Network APIs reference — every endpoint’s parameters, response schema, and error contract.