OTP approvals
OTP approvals is the second-supervisor gate for the two highest-leverage pieces of Orbit Verify:- an OTP message template going live, and
- a resend burst on an existing verification (a flurry of resend requests against one verification).
The policy
The policy has three fields:
Owners and admins edit the policy from the Policy card on the OTP approvals page (dashboard), or over the API.
Read the policy
GET /api/v1/verify/approvals/settings is readable by any authenticated member, so a requester can tell whether their next submission will queue before they send it.
require_approval_default: false) and an unset threshold (null) are the defaults — the gate is off until an owner or admin flips it on.
Update the policy
PUT /api/v1/verify/approvals/settings takes the policy row with your changes. Owners and admins only.
require_approval_default on every update — it is required. resend_burst_threshold accepts a positive integer or null (gate every burst); resend_burst_window_seconds accepts a positive integer up to 86,400 and defaults to 60 when omitted.
Work the queue from the dashboard
Open Outbound → OTP approvals. The page shows the pending queue and the Policy card on one surface:- Each row carries the kind (template or resend burst), the requester, and the queued timestamp, oldest first.
- Approve and reject controls sit on the pending row. Reject opens a dialog for an optional
reasonthat goes back to the requester. - Template rows show the exact rendered OTP body the approver is judging; resend-burst rows show the burst count and window the request accumulated.
Role restriction: only owners and admins read the queue, decide items, or edit the policy. Other roles see an access-restricted notice and the API returns 403.
Work the queue over the API
Three endpoints cover the queue: list it, approve an item, reject an item.List pending items
GET /api/v1/verify/approvals/pending returns the queue oldest first. Optional query parameters: limit (1–100, default 50) and kind (otp_template or resend_burst).
Approve an item
POST /api/v1/verify/approvals/:id/approve releases the queued work. No body required.
404. If the approver is the requester, the API returns 409 (see The self-approve rejection).
Reject an item
POST /api/v1/verify/approvals/:id/reject holds the item. The body is optional; reason goes back to the requester.
Role failure mode
A member without the owner or admin role gets403 on the queue, the decision endpoints, and PUT /settings (GET /settings stays readable so requesters can predict the gate):
Lifecycle of a pending item
Every item starts aspending and leaves the queue in exactly one of two ways:
- Approved → released. A gated template goes live: the rendered body is applied to its verification profile. A gated resend burst re-enters the ordinary Verify dispatch chain — the same path a direct resend takes — and still respects the verification’s expiry, channel fallback, and rate limits. Approval decides nothing more than the policy gate; it never touches the delivery path itself.
- Rejected → held. The item stops. With an optional
reasonthe requester sees why. A rejected resend burst is simply not delivered; the requester may re-submit it (the re-submission queues a fresh row).
Read a pending item
A queued resend burst looks like this (the same shape the pending endpoint returns per item):otp_template rows, rendered_body carries the exact template copy the approver judges, template_id names the verification profile it lands in, and channel names the profile slot it rewrites. For resend_burst rows the approver decides against the burst_count / window_seconds pair.
The self-approve rejection
If the approver is the same user as the requester, the API returns409 Conflict:
Audit log and webhooks
Every queue decision and every policy change lands in the Audit log with the actor, the queue item or organization, and the optional reject reason:verify.approval.approved— a supervisor released an item, recorded after the release succeeds.verify.approval.rejected— a supervisor held an item, with thereasonin the detail payload.verify.approval.settings.updated— someone edited the policy; the new values are in the detail payload.
verification.sent webhook as it dispatches, and an approved template shows up in the ordinary verification events of its next transmission — subscribe to the standard Verify events for downstream notification. See Webhook Events.
Troubleshooting
Still stuck? See the Verify overview and the Troubleshooting index.