> ## Documentation Index
> Fetch the complete documentation index at: https://docs.orbit.devotel.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Verify Configuration console: create and manage verification profiles

> Build, edit, bind, and retire verification profiles from the Configuration tab on the Verify page — the operator console for the profile that every other Verify guide touches.

# Manage verification profiles from the Configuration console

The **Verify → Configuration** tab is the profile-management surface of the Verify family. It lists every verification profile your organization owns ("Verification Profiles — Manage Your Verification Profiles") and opens each one for create/edit through a guided dialog. The rest of the Verify surface is then built on top of those profiles: the console page walks a verification row into a fraud-review state, and the API send path runs against a profile's saved rules.

This page is the name-firm center of that: create a profile from the console, edit the profile card, bind it to a send, and gate a profile change through the two-officer flow.

## 1. Where this page sits in the Verify family

The Verify pages split into three views, and this one is the **profile-management view**:

* **[Verify console: fraud gate, fraud score, and fallback forensics](/guides/verify-console)** — the operator fraud-review view: the on-demand Fraud Score and Fraud Gate dialogs, the code-attempts log, and the fallback execution timeline for a single verification row.
* **[Verify profiles: fallback chains and advanced factors](/guides/verify-fallback-chains)** — the API-side walk of a profile: the fallback chain `channels[]`, code settings, templates, and the factor suite that runs beside it.
* **Verify → Configuration (this page)** — the operator profile-management view: the five-step Create Profile wizard, per-profile edit dialogs, and the card grid those dialogs feed.

Open it from **Outbound → Verify → Configuration**. The configuration page at `/:locale/verify/configuration` redirects to **Verify** with the Configuration tab selected, so deep links land in the tab-embedded surface rather than a 404.

<Note>
  Profile writes under `GET|POST|PATCH|DELETE /api/v1/verify/profiles` are rate-limited at the tenant's read/write scopes, and every dashboard mutation also enforces a caller role — an owner, admin, or developer role is required to write. A profile change on the console is a tenant-owned control; nothing in this guide mandates a platform-side gate.
</Note>

## 2. What a verification profile contains

A profile is a **tenant-owned** configuration record that controls how OTP verifications behave for sends that carry its id. The console tab and the API schema expose the same fields; the wizard below writes them all.

| Group                 | What it holds                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 |
| --------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Name & channels**   | The profile's display name, and the ordered channel chain `channels[]` (SMS, WhatsApp, email, voice, Viber, Telegram, flashcall, silent, SNA, TOTP, push, magic-link, backup-code). Index 0 is the primary channel the first send attempt uses; the rest are the async fallback order.                                                                                                                                                                                                                                                                                        |
| **Code settings**     | `codeLength` (4, 6, or 8 digits), `expirySeconds` (60–3600s TTL), `maxAttempts` (1–10).                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       |
| **Templates**         | A per-channel body override map. Each body may reference the `{{code}}`, `{{app_name}}`, `{{expiry_minutes}}`, `{{expiry}}`, and `{{magic_link_url}}` placeholders — see [Verify profiles: fallback chains](/guides/verify-fallback-chains#3-code-length-ttl-and-templates) for the full placeholder contract.                                                                                                                                                                                                                                                                |
| **Fraud guard**       | Geo mode (`allowlist`, `blocklist`, `none`) plus a country list, the velocity caps (`velocityPerHour` up to 100/h, `velocityPerDay` up to 1000/d), an IP-restriction CIDR list (up to 50 ranges), an optional MCC/MNC blocklist (up to 200 codes), and composite-risk thresholds (`riskStepUpThreshold` must be at/below `riskBlockThreshold`). The risk-adaptive `channelEscalation` block can also remap the chain on an under-threshold composite score — enable it, name `weakChannels`, then let the guard swap to the strict-side `preferredChannels` on a scored send. |
| **Cost optimization** | A toggle `costOptimization.enabled`, and an optional per-cascade cap `maxCascadeCostCents` (1–100,000) that stops a chain's cumulative spend above the ceiling.                                                                                                                                                                                                                                                                                                                                                                                                               |
| **Webhook**           | One URL the profile's verification events deliver on. Only `https://` (or localhost `http://`) is accepted; other schemes (ftp, ws) are rejected at save — kept active so a broken integration can't sit silently in an active state.                                                                                                                                                                                                                                                                                                                                         |
| **Status**            | `active` or `inactive`. Create inactive to park a profile until you flip it on; a deactivated profile is blocked for new sends.                                                                                                                                                                                                                                                                                                                                                                                                                                               |

<Note>
  **Pair voice biometrics beside the chain, not inside it.** The Verify page's voice-biometrics module is a separate surface — enroll a voiceprint, run a check, and set the confidence threshold — and it does not appear as an entry in `channels[]`. An OTP chain carries the delivery channels a voice check can fall back to. The module itself lives on the **Verify → main** page; pair it with a profile by keeping the same tenant's settings card open beside the profile, not by trying to reorder biometrics into a fallback step.
</Note>

<Tip>
  The **two-officer flow** that can gate a profile's template change is an org-level policy under `verification` settings, not a field saved per-profile. Enable it once for the org and the gate applies uniformly to any non-supervisor save on this page (see §6).
</Tip>

## 3. The create-profile wizard, step by step

Click **Create Profile** on the Configuration tab header. The dialog is a **five-step wizard** — **Basic Info → Code Settings → Templates → Fraud Prevention → Webhook** — and each step validates before **Next** advances, so a blank `name` (or an above-order step-up risk threshold) can't be skipped to the final submit. The step indicator only green-checks a step once you've moved forward from it error-free; unvisited future steps are inert (disabled rather than merely non-responsive). The dialog also locks its close affordances while a create/update is in flight so an Escape or outside click can't cancel a pending save and leave a toast telling the wrong story.

1. **Basic Info** — name the profile (1–100 characters) and order the channel chain. The channel list is a drag-to-reorder list with per-channel **move-up / move-down** buttons and a numbered `A → B → C` order display; each channel chip is labelled with the channel it renders. The wizard must list at least one channel; the API rejects a `channels` member whose provider isn't connected (`422 WHATSAPP_NOT_CONNECTED` and similarly named envelopes per channel).
2. **Code Settings** — pick the code length (4 / 6 / 8), set `expirySeconds` (60–3600), and set `maxAttempts` (1–10).
3. **Templates** — for every channel in the chain, write the body in the channel's template field. A channel template that is set must contain the `{{code}}` placeholder; the wizard chips it inline if the mandatory token is missing, and an empty template falls through to the server-side default, so empty is allowed. WhatsApp is exempt — its `templates.whatsapp` value is the *name* of a Meta-approved authentication template selected through a dedicated picker — and missed-call/factor channels (flashcall, SNA, TOTP, push, magic-link, backup-code) are exempt by construction.
4. **Fraud Prevention** — choose the geo mode (allowlist / blocklist / none) and countries, the velocity caps, any IP-restriction CIDR list, and the composite-risk Fraud Guard levers (MCC/MNC blocklist, risk-step-up threshold `≤` risk-block threshold, a channel-escalation remap block). The wizard's own geo-set state survives navigation between steps instead of resetting on the step unmount.
5. **Webhook** — one delivery URL for this profile's verification events; https (or http on localhost). Finish with **Create verification profile**.

On success the profile lands as a card in the grid and the success toast carries an **"Integrate"** action that jumps to the Developer quickstart — a profile is useless until a send carries its id.

<Warning>
  **Every save writes an immutable audit record.** Create, edit, and delete each write a `verification_profile.created` / `.updated` / `.deleted` audit entry tagged with the operator, and the two-officer gate in §6 writes its own audit row on an approve/reject decision. The audit hook is fire-and-forget — a failed write logs a warning but never blocks the save.
</Warning>

## 4. Read — and edit — a profile card

Back on the grid, a profile card compresses the profile into grouped chips so you read its **effective chain** and **fraud-gate settings** without opening the dialog:

* **Channels** — channel chips ordered as the chain is ordered (the same ordering the API fallback engine consumes).
* **Fraud guard** — a geo chip ("Allowlist: DE, FR" / "Blocklist: XX" / a full "No geographic restrictions" tooltip), the velocity chip ("Velocity limit: 20 sends per hour, 100 per day", or "No velocity limits" when off), and the IP chip ("IP allowlist: 203.0.113.0/24, …", or "No IP restrictions — all source IPs allowed") when the list is set.
* **Linked module** — the webhook footer shows whether the profile carries a delivery URL; the paired biometrics enablement stays on the **Verify → main** page beside the profile, as noted above.

Open **Edit verification profile** from the card header. The dialog re-runs the same five wizard steps pre-filled with the profile's current values, and PATCHes any subset over `PATCH /api/v1/verify/profiles/{id}`. Put a profile's `status` to `inactive` to stop new sends without deleting it.

## 5. Bind the profile to a send

Pass the profile id on each send that should follow the profile's rules:

```bash theme={null}
curl -X POST "https://api.orbit.devotel.io/api/v1/verify/send" \
  -H "X-API-Key: $ORBIT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "to": "+14155552671",
    "channel": "sms",
    "profile_id": "vprof_7a1c0e2b…"
  }'
```

A `/verify/send` body that carries **no** `profile_id` can override every saved rule inline — pass ad-hoc `channels` and a `fallback_config` block (per-channel `channel_timeout_seconds`, 10–600s, default 60s) in the same body and the send synthesizes a transient profile in place of a stored one. When a stored `profile_id` is present **the profile wins** — the inline override is skipped and the stored chain is re-loaded. The profile also constrains the send's `channel` — passing a channel that isn't in the profile chain is rejected with a `422` naming the allowed channels — so bind the profile **and** the same first channel out of its chain.

<Note>
  The `costOptimization.enabled` toggle is the third lever here. When a profile turns it on, the send engine re-orders the chain at each advance to try the cheapest route first, with `maxCascadeCostCents` capping the cascade's cumulative spend. It's a profile tenant-owned control — the send honors it only when the bound profile has it set.
</Note>

## 6. Two-officer flow — submit a change for approval

When your organization's **verify two-officer** policy is on (org-level settings under `verification`), a non-supervisor save on this page does not apply the change — the dialog's save is **gated into the approvals queue** instead. The gate exists for two cases:

* **OTP template change** — a per-channel body a non-supervisor tried to write on create or edit. The queue row of kind `otp_template` carries the rendered body inline so the supervisor judges the exact copy the recipient would see.
* **Resend burst** — a second Nth send of the same code inside the org's resend-burst window. Queue row kind `resend_burst`.

The supervisor queue answers over the same plugin-mounted routes the [OTP approvals two-officer guide](/guides/verify-otp-approvals-workflow) walks through:

| Route                                        | Use                                                                                             |
| -------------------------------------------- | ----------------------------------------------------------------------------------------------- |
| `GET /api/v1/verify/approvals/pending`       | Supervisor queue — oldest first                                                                 |
| `POST /api/v1/verify/approvals/{id}/approve` | Four-eyes enforced — the approver must differ from the requester; a self-approve returns `409`  |
| `POST /api/v1/verify/approvals/{id}/reject`  | Rejects with an optional `reason`                                                               |
| `GET /api/v1/verify/approvals/settings`      | The org policy — any authenticated caller can read it so a gated save never comes as a surprise |
| `PUT /api/v1/verify/approvals/settings`      | Owner/admin updates the policy                                                                  |

<Info>
  The replay semantics depend on the queue row's kind. An **OTP-template** approval re-reads the profile's current row and applies the approved rendered body — the queue row references the profile by template id and the replay is a plain profile update run on the supervisor's behalf. A **resend-burst** approval replays the resend-handler the `/verify/:id/resend` route takes; if the replay throws, the row stays `pending` and the supervisor can retry. Either kind writes an immutable audit entry for the decision — the same append-only audit hook every profile save already uses.
</Info>

## 7. API parity

Everything above runs against the same control surface the API exposes — the console just picks it up over the dialog:

| Method   | Path                           | Use                         |
| -------- | ------------------------------ | --------------------------- |
| `GET`    | `/api/v1/verify/profiles`      | List your profiles          |
| `GET`    | `/api/v1/verify/profiles/{id}` | Get one profile             |
| `POST`   | `/api/v1/verify/profiles`      | Create a profile            |
| `PATCH`  | `/api/v1/verify/profiles/{id}` | Update any subset of fields |
| `DELETE` | `/api/v1/verify/profiles/{id}` | Delete the profile (204)    |

The `/configs` paths are an alias of `/profiles` with identical behavior — use whichever your integration already references. Profile **reads** default to the tenant read rate limit; **writes** run behind the write-rate-limit + write-guard the dashboard passes.

The canonical per-endpoint schemas live under the [Verify API reference](/api-reference/endpoints/verify). Walk the full send/check lifecycle beside the profile on the [Verify profiles: fallback chains](/guides/verify-fallback-chains#6-send-and-check-against-the-profile) guide.

## Troubleshooting

| Symptom                                                                         | Cause                                                                             | Fix                                                                                                                                                                                            |
| ------------------------------------------------------------------------------- | --------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Save returns `{ gated: true, kind: "otp_template" }` instead of a clean success | The org's two-officer policy is on and your role is not a supervisor              | The queued request now waits on the supervisor queue. A supervisor must open `GET /approvals/pending` and approve/reject; a self-approve is rejected with `409`.                               |
| Save returns `403` or the dialog reports a write-guard scope                    | The caller's role is not owner / admin / developer on this route                  | Have an owner/admin flip your role, or run the profile mutation over an API key with `verify:write`.                                                                                           |
| `409` on Approve                                                                | The approver's user id equals the requester's                                     | Have a second supervisor (owner/admin) operate the second pair of eyes.                                                                                                                        |
| Profile left `inactive`, sends report inactive                                  | The profile was parked in the dialog and never re-activated                       | Open **Edit verification profile** and flip the status back to `active`.                                                                                                                       |
| In-flight verification ignores a profile edit                                   | A running verification already snapshotted the profile's chain at send            | Open the verification's detail drawer and `POST /verify/{id}/cancel` the send (or let it expire), then resend against the edited profile. The profile edit can't retrofit the current row.     |
| `DELETE` the profile returns 204 but a send still references it                 | The send bound to the deleted id falls back to defaults — there is no safety stop | Re-load a fallback for in-flight rows that already loaded the profile's rules; resend future requests with a different profile. Binding a profile to a send is a contract you break on delete. |

<Note>
  **Sovereign compliance controls.** The geo allowlist/blocklist, IP restriction list, MCC/MNC blocklist, velocity caps, and risk thresholds above are tenant-owned — they constrain verification posture for your organization only, and do not impose a platform-wide country or OFAC gate beyond your own policy.
</Note>

## See also

* [Verify overview](/verify/overview) — send model, the channel catalog, and rate limits.
* [Verify console: fraud gate, fraud score, and fallback forensics](/guides/verify-console) — the operator fraud-review view this page's profiles feed.
* [Verify profiles: fallback chains and advanced factors](/guides/verify-fallback-chains) — the API-side walk of the same profile.
* [OTP approvals workflow](/guides/verify-otp-approvals-workflow) — the four-eyes queue this page gates a template change into.
* [Two-officer approval gates](/guides/verify-approvals-two-officer) — the same control, combined.
* [Verify API reference](/api-reference/endpoints/verify) — profile CRUD and send/check endpoints.
