Port-out PIN protection lifecycle
Port-out PIN protection is the tenant-owned control that makes a stolen API credential insufficient to move a number off your account. It is a per-number secret you set yourself; Devotel Orbit verifies it before any port-out reaches the carrier. This page models the PIN’s lifecycle — set, rotate, remove — and how it slots into the wider port-out flow. The port-out state machine it protects is Port-out lifecycle; the runbook procedure is Port numbers out safely.Why port-out needs its own control
Every other number lifecycle exit — release, suspend, park — returns the number to inventory or freezes it. Port-out is different: it transfers ownership to another carrier. That makes it the one operation where a caller holding your API key can permanently take a number away, the port-hijack attack class. Reversing a mistaken release means re-buying a DID that may still be in stock; reversing a completed port-out means negotiating a port-back with a carrier you do not control. The PIN closes that gap with a second factor only the organization that owns the number can set — the same “account PIN” model carriers surface under the FCC port-out-protection rule (47 CFR § 64.6100).The self-serve PIN lifecycle
Three operations, all under the number’s own path, all restricted to the organization that owns the DID — sibling organizations in a shared workspace cannot read, set, or strip each other’s PINs (a miss surfaces as a404, never a 403, so existence cannot be probed across
siblings):
- Set —
POST /numbers/:id/port-out-protectionwithport_out_pinin the body. The PIN is scrypt-hashed with a per-number salt the moment it arrives. The response confirms only{ enabled: true, set_at }. - Rotate — the same
POSTreplaces the existing PIN with a fresh hash and salt. Rotation is also the recovery path: because the PIN is one-way hashed and never readable, a lost PIN is replaced, not recovered. - Remove —
DELETE /numbers/:id/port-out-protectionclears the hash and salt entirely, returning the number to opt-out.
422 VALIDATION_ERROR before anything is
written. Readable state is deliberately minimal:
GET /numbers/:id/port-out-protection returns
{ enabled, set_at } and nothing else — the plaintext value is
unobservable by design.
When the PIN is required: opt-in, per number
Protection is opt-in on a per-DID basis, and that choice shapes the whole verification model:- No PIN set → verification auto-passes. A port-out on an
unprotected number proceeds without a
port_out_pinin the body. The absence of protection is not a rejection — you opt in on exactly the numbers whose loss would hurt. - PIN set → verification is mandatory. Every port-out submission
(
POST /numbers/:id/port-out, and the batchPOST /numbers/porting/out) must carryport_out_pin, and for a batch it must match every protected number in the request, or the whole batch is refused before any carrier call.
Concurrency: pending orders and PIN changes
A number inport_out_pending already has a live carrier order behind
it, so two interactions matter:
- New port-outs are blocked. Any further port-out submission — PIN
correct or not — returns
409 CONFLICTwhile the order is in flight. The PIN gate and the pending-order gate stack; both must pass. - Rotate and remove stay available. The PIN surface itself is not
frozen by a pending order. That is deliberate: if a PIN was exposed
mid-port (a leaked credential or a departed operator), you must be able
to rotate it without cancelling the order first. Rotation while
port_out_pendingdoes not affect the in-flight order — the verify happened at submission time, and downstream status updates run on the order id, not the PIN.
Two directions, one object
The unified number portability model treats port-in and port-out as directions of one portability object. The PIN lives squarely on the outbound direction: it is an ownership assertion made by the organization that currently holds the DID, checked on the losing side of the transfer. Port-in carries no PIN at all — its proof of ownership is the LOA and account evidence on the winning side, and a PIN on an external number is meaningless to Orbit. When you read the unified model, the PIN is the outbound-side answer to “prove this is my number leaving,” just as the signed LOA is the inbound-side answer to “prove this number may arrive.”Failure modes
Verification compares hashes with a timing-safe equality check, so the
response time does not leak whether a guess was closer to the stored PIN.
Each set, rotate, and remove is written to your organization’s audit log
with the lifecycle event only — the plaintext never appears in logs,
responses, or audit details.
Cross-links
- Port-out lifecycle — the
port_out_pendingstate machine, status gates, and events this PIN gates entry into. - Number portability model — the one-object, two-direction portability vocabulary.
- Port numbers out safely — the operational runbook with endpoint examples.
- Number lifecycle — the status enum
port_out_pendingbelongs to. - Webhook events — the
number.port_out.*payloads.