Skip to main content

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 a 404, never a 403, so existence cannot be probed across siblings):
  • SetPOST /numbers/:id/port-out-protection with port_out_pin in 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 POST replaces 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.
  • RemoveDELETE /numbers/:id/port-out-protection clears the hash and salt entirely, returning the number to opt-out.
A PIN is 4–32 characters of alphanumerics or common punctuation; a rejected format returns 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_pin in 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 batch POST /numbers/porting/out) must carry port_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.
Because the check is per-DID, an operator can protect the revenue-bearing DIDs while leaving test or staging numbers unprotected, and can adopt the control incrementally without a migration across the inventory.

Concurrency: pending orders and PIN changes

A number in port_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 CONFLICT while 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_pending does 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.