Human Agent Portal API
The Human Agent Portal is the surface your people sign into — the live agents who take over a call when the AI hands off. This page covers what you drive from your own backend with your organization key: provisioning those agents' portal credentials, and reading their presence, attendance and shifts.
Base URL:
https://your-infravoice-host/api/v1Auth header:Authorization: Bearer usf-…(your organization key, from Account → Organization key) Response envelope: every route here returns the standard Go envelope{"success":true,"data":{…},"error":null,"meta":…,"timestamp":…}. The shapes shown below are thedatafield.
"Agent" here means a human, not an AI agent. A human agent is a person with an agent code and a password; an AI agent is the configured voice bot documented elsewhere. Nothing on this page reads or writes AI agent configuration, so the draft-and-publish workflow that governs AI agent config does not apply — every call on this page takes effect immediately.
The agents themselves sign in through the portal application, which holds its own session for them. You provision the credentials here; they use them there.
Endpoints
| Method | Endpoint | Purpose |
|---|---|---|
POST | /api/v1/agent-portal/admin/set-password | Set or replace an agent's portal password. |
POST | /api/v1/agent-portal/admin/reset-password | Generate a temporary password, optionally emailing it. |
POST | /api/v1/agent-portal/admin/send-credentials | Email an agent their slug, code and password. |
GET | /api/v1/agent-portal/admin/password-audit | Password action history for one agent. |
GET | /api/v1/agent-portal/admin/presence/realtime | Live presence board for a workspace. |
GET | /api/v1/agent-portal/admin/presence/attendance | Attendance summary over a date range. |
GET | /api/v1/agent-portal/admin/presence/timeline/{agentID} | Raw presence event log, one agent, one day. |
GET | /api/v1/agent-portal/admin/presence/alerts | Shift-adherence alerts for a workspace. |
POST | /api/v1/agent-portal/admin/presence/shifts | Create or upsert a weekly shift. |
GET | /api/v1/agent-portal/admin/presence/shifts/{agentID} | List an agent's shifts. |
DELETE | /api/v1/agent-portal/admin/presence/shifts/{shiftID} | Delete a shift. |
GET | /api/v1/agent-portal/health | Liveness. Also /healthz, /ready, /live. |
Nothing here is billed per call. The portal's own endpoints consume no credits; the telephony and voice usage that happens because of a handoff is billed on those services' own terms.
Create the human agent records themselves with POST /api/v1/call-center/agents — see the Call Center page — then set their password here.
Admin (owner-side)
Everything under /api/v1/agent-portal/admin/* takes your usf- organization key. Despite the /admin/ path these are ordinary workspace-owner functions, not superadmin: the caller's workspaces claim must include the target agent's workspace, and an agent or workspace_id belonging to someone else is rejected.
Credentials
set-password takes {agent_id, password} (minimum 8 characters) and returns {"status": "password_set"}. Use it when provisioning agents programmatically and delivering the password yourself.
reset-password takes {agent_id, send_email}:
curl -X POST https://your-infravoice-host/api/v1/agent-portal/admin/reset-password \
-H "Authorization: Bearer usf-YOUR_ORG_KEY" \
-H "Content-Type: application/json" \
-d '{"agent_id": "3f6c1b2e-8d4a-4c19-9b77-2a5e0c11d904", "send_email": true}'{
"email_sent": true,
"message": "Temporary password emailed to the agent"
}With send_email: false the generated temp_password comes back in the response body. That is a live credential in plaintext — never log it, never echo it into a UI you do not control, and never write it to disk.
send-credentials takes {agent_id, temp_password} and emails the agent the three things they need to log in. It returns {"status": "email_sent"}, 400 NO_EMAIL if the agent has no email address, and 503 SMTP_DISABLED if mail is not configured on the deployment — so a 200 here, unlike forgot-password, really does mean an email was dispatched.
password-audit takes a required ?agent_id=<uuid> and returns the action history — id, action, performed_by, performed_by_id, ip_address, details, created_at — covering every set, reset and credential email for that agent.
Presence and attendance
These back the workforce-management views in the dashboard.
realtime requires ?workspace_id=<uuid> and returns one row per active human agent with id, display_name, agent_code, status, status_changed_at, updated_at and max_concurrent_calls:
curl "https://your-infravoice-host/api/v1/agent-portal/admin/presence/realtime?workspace_id=b21f7e8c-5a44-4f0e-9d2c-77c1e3a90f18" \
-H "Authorization: Bearer usf-YOUR_ORG_KEY"attendance takes ?workspace_id= plus optional from, to (inclusive YYYY-MM-DD dates, both defaulting to today) and agent_id, and returns one row per agent per UTC day: id, agent_id, display_name, agent_code, date, the durations online_seconds, available_seconds, busy_seconds, on_call_seconds, wrap_up_seconds and break_seconds, the counters session_count and break_count, first_login / last_logout, and — only for a day the agent has a shift on — scheduled_start, scheduled_end, late_login_seconds, early_logout_seconds and shift_adherence_pct. Days are computed from the agent's own session log, so two edges are worth knowing: time from a session that began before the range is credited to the days it covers, but its first_login and session_count stay with the day the agent actually logged in; and a session still open right now is counted up to now, not to the end of the day. timeline/{agentID} takes an optional ?date=YYYY-MM-DD (defaulting to today) and returns the raw event stream — id, event_type, source, session_id, ip_address, details, created_at — which is where the connect, disconnect and tab-visibility events sent from the client end up. alerts takes ?workspace_id= and an optional ?unacknowledged=true, returning at most 100 rows newest first, each with alert_type, severity, message, details and acknowledged.
Shifts are what make the alerts possible. POST /admin/presence/shifts takes {agent_id, day_of_week, shift_start, shift_end, timezone?} — timezone defaults to UTC — and upserts on the agent, day and start time, so re-posting the same shift edits it rather than duplicating it. Adherence is evaluated automatically whenever an agent connects or disconnects.
Errors
Errors use the same envelope: {"success": false, "error": {"code": "…", "message": "…"}, "timestamp": "…"}.
| HTTP | Code | Meaning |
|---|---|---|
| 400 | INVALID_BODY | Body was not valid JSON. |
| 400 | INVALID_PARAM | attendance only: from/to was not a YYYY-MM-DD date, or to preceded from. |
| 400 | INVALID_TOKEN | Reset token used, revoked, or past its 1-hour expiry. |
| 400 | NO_EMAIL | send-credentials for an agent with no email address. |
| 401 | UNAUTHORIZED | Missing, malformed or revoked organization key. |
| 403 | FORBIDDEN | The agent or workspace belongs to another organization. |
| 429 | — | Rate limit; a Retry-After header is set. |
| 500 | LOGIN_FAILED, TOKEN_FAILED, UPDATE_FAILED, QUERY_FAILED | Server-side failure at the named step. |
| 502 | UPSTREAM_UNAVAILABLE, SCOPE_FAILED | A service this route proxies to was unreachable. |
| 503 | SMTP_DISABLED | Mail is not configured on this deployment. |
Requests are rate limited per client IP as a token bucket refilling at 100 requests per second with a burst of 200; a 15-second heartbeat and a few seconds' dashboard poll are nowhere near it, but a tight retry loop after a 502 can be. The service also applies a 30-second request timeout — every route here is a small database read or write plus, in a few cases, one short internal proxy hop, so a request approaching that ceiling means an upstream problem, not a slow query.