> ## 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.

# Skill certifications: issue, renew, and revoke agent credentials

> Manage the expiry-dated per-skill certifications that gate regulated queues (HIPAA, FINRA, insurance producer, licensed counsel) — run the expiry sweep, issue or renew a certification, revoke one, and read the audit trail.

# Skill certifications for regulated queues

Regulated queues — healthcare (HIPAA), securities (FINRA Series 6/7/63), insurance producer, licensed counsel — should not take calls from every agent who has the skill tag. The **Voice → Skill certifications** page (/voice/skill-certifications) gives supervisors the issue, renew, and revoke controls for the expiry-dated per-skill certification that gates those queues.

The certification layer sits on top of skills-based routing:

1. The agent's **skills** list decides what they can do at all. A certification never grants a skill — it only layers an expiry window on top of one the agent already holds.
2. The certification's **expiry window** decides whether the agent is still eligible for queues that require that skill.
3. At each dispatch tick the matcher excludes any agent whose certification for the required skill has lapsed. The agent remains eligible for queues that do not require it.

A certification with no expiry date (`certifiedUntil: null`) never lapses. An expired one takes effect at the **next dispatch tick** — exclusion is automatic, there is no enroll/withdraw step.

## Run the expiry sweep

The sweep answers: which certifications lapse in the next N days? Run it before every renewal batch — the page lists windows ordered by soonest expiry first.

1. Open **Voice → Skill certifications**.
2. Pick the status filter: **Active**, **Expired**, or **All states** (default).
3. Enter a horizon in the **expiring within N days** field — for example, 30 for a monthly renewal cycle.
4. Widen the time window for expired rows by raising the **expired lookback** control above its default 30 days.

<Frame>
  <img src="https://mintlify.s3.us-west-1.amazonaws.com/zeelaltd/images/guides/skill-certifications/expiry-sweep.png" alt="The certifications table filtered to status Active with a 30-day expiry horizon, ordered by soonest certifiedUntil" />
</Frame>

The table rows agent, skill, optional proficiency level (1–5), and the certification window with a computed **Expired** flag. Search narrows the list by agent name. Over the API the same sweep is one GET:

```bash theme={null}
curl "https://orbit.devotel.io/api/v1/voice/skill-certifications?status=active&expiringWithinDays=30" \
  -H "X-API-Key: dv_live_sk_your_key_here"
```

The sweep paginates with a keyset cursor — follow `meta.pagination.cursor` until `has_more` is false. Page size defaults to 200 and clamps at 500.

## Issue a new certification

1. On the certifications page, click **Issue certification**.
2. Select the agent, the registered skill slug, an optional proficiency level (1–5), and the expiry date.
3. Submit. The new window activates immediately — the agent is eligible again at the next dispatch tick.

<Frame>
  <img src="https://mintlify.s3.us-west-1.amazonaws.com/zeelaltd/images/guides/skill-certifications/issue-modal.png" alt="The Issue certification dialog with agent select, skill slug, optional level, and expiry date fields" />
</Frame>

The API call is an idempotent upsert on the (agent, skill) pair — an agent with one skill has at most one window, and re-issuing renews it:

```bash theme={null}
curl -X POST https://orbit.devotel.io/api/v1/voice/skill-certifications \
  -H "X-API-Key: dv_live_sk_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "agentUserId": "5f3c0a4b-...",
    "skill": "licensed-counsel-triage",
    "level": 4,
    "certifiedUntil": "2026-12-31T23:59:59Z",
    "certifiedFrom": "2026-01-01T00:00:00Z"
  }'
```

The skill must be a registered routing skill in the tenant's active catalog. An unregistered slug returns `422 UNKNOWN_SKILL` rather than persisting a dead row that never affects dispatch. `certifiedUntil: null` — or omitting the field — means the certification never expires.

## Renew or revoke

Each row carries **Renew** and **Revoke** actions.

* **Renew** extends or amends the window. Patch any of `level`, `certifiedUntil`, `certifiedFrom`. Omitted fields keep their current values; explicit `null` clears a field (for example, set `certifiedUntil: null` to move an agent to never-expires).
* **Revoke** takes the agent off queues requiring the skill at the next dispatch tick by lapsing the window immediately. The certification row stays on file — deleting a row would read as *no recertification requirement, effective forever*, which is the opposite of a revocation.

```bash theme={null}
# Renew — extend the window to a new expiry
curl -X PATCH https://orbit.devotel.io/api/v1/voice/skill-certifications/cert_2b8c1d \
  -H "X-API-Key: dv_live_sk_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{ "certifiedUntil": "2027-06-30T00:00:00Z" }'

# Revoke — lapse immediately
curl -X DELETE https://orbit.devotel.io/api/v1/voice/skill-certifications/cert_2b8c1d \
  -H "X-API-Key: dv_live_sk_your_key_here"
```

A PATCH with no fields returns `422`; at least one of `level`, `certifiedUntil`, `certifiedFrom` is required. `certifiedFrom` after `certifiedUntil` is rejected to guard against inverted windows.

## How agents re-enter routing

Re-entry is not a separate enrollment step — it follows the next renewal:

1. A certification expires. At the next dispatch tick the matcher skips the agent for any queue requiring that skill; unrestricted queues keep routing to them.
2. A **Renew** on the row (or a fresh **Issue** upsert on the same agent+skill) restores eligibility. The next dispatch tick after the renewal picks it up — the change is near-immediate, not a stale-cache event.
3. Until then, a caller queued on the regulated queue may wait or fall through to your fallback handling — no error is raised, and no alert fires, which is exactly why the expiry sweep matters: treat renewals as a batch run timed to your recertification cycle (for example, 30 days before expiry), not something you do reactively after exclusion.

## Audit trail and compliance

Every issue, renew, and revoke writes to the audit ledger — the full **who, when, agent, skill** record, and for renewals the **before/after** values of `level` and `certifiedUntil`. This is what makes regulated-queue membership provable to an auditor, and it complements the audit actions that live-monitoring records on the calls themselves.

Keep the expiry-sweep and revoke controls restricted to supervisor and admin roles, as the page does. This is a tenant-owned control of the kind described by the [SOC 2 controls](/compliance/soc2-controls) posture — responsibility for who touches a credential rests with the tenant, and the ledger makes that accountability reviewable.

Related guides:

* [Voice queues](/guides/voice-queues) — skills-based routing these certifications gate.
* [Supervisor live monitoring for voice](/guides/supervisor-live-monitoring-voice) — per-call audit actions (listen, whisper, barge).
* [Audit log](/guides/audit-log) — browsing and exporting the ledger.
* [SOC 2 controls](/compliance/soc2-controls) — tenant-owned access controls as part of the overall compliance posture.
