AI Employees — API reference
AI Employees — API reference¶
The seven methods @opvs-ai/employees exposes. Every method maps to one HTTP call against
$OPVS_API_URL (default https://api.opvs.ai), authenticated as a bearer token in $OPVS_PAT
unless the method is marked public.
Two of the seven are public and unauthenticated. The other five resolve the brand from the token, so a token pinned to one brand can never read another brand's team.
| Method | HTTP | Auth |
|---|---|---|
catalog_search |
GET /api/v1/employees/published |
public |
employee_get |
GET /api/v1/employees/{slug} |
public |
team_list |
GET /api/v1/employees/hires |
PAT, brand-scoped |
hire_get |
GET /api/v1/employees/hires/{hire_id} |
PAT, brand-scoped |
hire |
POST /api/v1/employees/{profile_id}/hire |
PAT, brand-scoped |
cancel |
POST /api/v1/employees/hires/{hire_id}/cancel |
PAT, brand-scoped |
equip_profession |
POST /api/v1/employees/hires/{hire_id}/equip |
PAT, brand-scoped |
catalog_search(query, category, featured, limit)¶
Browse the published AI Employee catalog. Filters by category, price band, featured flag, or a keyword. Returns the public profile for each — never the persona template behind it.
Public and unauthenticated: no token is required, and no brand is resolved.
| Parameter | Type | Required | Notes |
|---|---|---|---|
query |
string | no | Keyword match across role and skills |
category |
string | no | Category slug, e.g. sales |
featured |
boolean | no | Restrict to featured profiles |
limit |
integer | no | Page size, default 20 |
curl -s "https://api.opvs.ai/api/v1/employees/published?query=sdr&limit=2"
# → {"profiles": [{"slug": "ai-sdr", "role": "AI SDR", "category": "sales",
# "price_band": "standard", "featured": true}], "total": 113}
Errors:
| Code | When | What to do |
|---|---|---|
422 |
A filter value has the wrong type, e.g. limit=abc |
Send limit as an integer |
503 |
The catalog database is unavailable | Retry with backoff; this is transient |
employee_get(slug)¶
Read one published employee's full product page: role, skills, portfolio, testimonials, work history and pricing. Public and unauthenticated, so it is safe to call before a brand exists.
| Parameter | Type | Required | Notes |
|---|---|---|---|
slug |
string | yes | The profile slug from catalog_search |
curl -s "https://api.opvs.ai/api/v1/employees/ai-sdr"
# → {"slug": "ai-sdr", "role": "AI SDR", "skills": ["prospecting", "outreach"],
# "portfolio": [], "testimonials": [], "pricing": {"band": "standard"}}
Errors:
| Code | When | What to do |
|---|---|---|
404 |
No published employee has that slug | Re-read the slug from catalog_search; unpublished profiles are invisible here |
503 |
The catalog database is unavailable | Retry with backoff |
team_list()¶
List the AI employees this brand has hired: one card per hire, with live status, the provisioned agent id, and what each one is equipped with.
The brand comes from the token, never from a parameter. There is no way to ask for another brand's team.
curl -s -H "Authorization: Bearer $OPVS_PAT" \
"https://api.opvs.ai/api/v1/employees/hires"
# → {"hires": [{"hire_id": "8f14e45f-ceea-467a-9c71-1b93a4f0d2c1",
# "status": "active", "agent_id": "sdr-brand-11"}], "total": 1}
Errors:
| Code | When | What to do |
|---|---|---|
401 |
The token is missing, malformed or expired | Re-issue a PAT with opvs auth request |
403 |
The token carries no brand pin, or lacks the employees scope | Mint a brand-pinned token with the employees scopes itemized |
503 |
The database is unavailable | Retry with backoff |
hire_get(hire_id)¶
Read one hire by id: its status, the agent it provisioned, and the packages equipped on it. This
is the poll target after hiring, since status moves from provisioning to active in the
background.
| Parameter | Type | Required | Notes |
|---|---|---|---|
hire_id |
uuid | yes | Returned by hire and by team_list |
curl -s -H "Authorization: Bearer $OPVS_PAT" \
"https://api.opvs.ai/api/v1/employees/hires/8f14e45f-ceea-467a-9c71-1b93a4f0d2c1"
# → {"hire_id": "8f14e45f-ceea-467a-9c71-1b93a4f0d2c1", "status": "provisioning",
# "agent_id": null, "equipped": []}
Errors:
| Code | When | What to do |
|---|---|---|
404 |
No hire with that id belongs to this brand | Confirm the id with team_list; a hire on another brand reads as absent, not as forbidden |
401 |
The token is missing or expired | Re-issue a PAT |
hire(profile_id)¶
Hire an AI employee onto this brand. This spends money and consumes a plan seat. It creates the hire, then provisions a real agent for it in the background, returning before provisioning finishes.
Hiring requires an active service plan. A brand with no plan gets a structured
403naming the limit it hit. This is a fail-closed quota gate, not a bug: a token caller is denied whenever no tariff rule resolves, so an agent cannot loop against an inert cap.
| Parameter | Type | Required | Notes |
|---|---|---|---|
profile_id |
uuid | yes | The catalog profile being hired |
curl -s -X POST -H "Authorization: Bearer $OPVS_PAT" \
"https://api.opvs.ai/api/v1/employees/3fa85f64-5717-4562-b3fc-2c963f66afa6/hire"
# → 403 {"detail": {"limit_type": "max_hires_per_brand", "current": 0, "max": 0}}
Errors:
| Code | When | What to do |
|---|---|---|
403 |
No active plan, or the brand is at its seat cap. Body carries {limit_type, current, max} |
Attach a service plan to the brand; read limit_type to see which cap bound |
429 |
More than the hourly hire limit for this brand | Back off and retry after the window |
422 |
The employee has no template configured, so it is not deployable | Pick a different profile; this one cannot provision |
404 |
The profile does not exist or is not published | Re-read the id from catalog_search |
cancel(hire_id)¶
Cancel a hire at the end of its current period. The agent keeps working until then and nothing is torn down immediately. Returns the effective date.
| Parameter | Type | Required | Notes |
|---|---|---|---|
hire_id |
uuid | yes | The hire to cancel |
curl -s -X POST -H "Authorization: Bearer $OPVS_PAT" \
"https://api.opvs.ai/api/v1/employees/hires/8f14e45f-ceea-467a-9c71-1b93a4f0d2c1/cancel"
# → {"hire_id": "8f14e45f-ceea-467a-9c71-1b93a4f0d2c1", "effective_at": "2026-09-13"}
Errors:
| Code | When | What to do |
|---|---|---|
404 |
No hire with that id belongs to this brand | Confirm the id with team_list |
409 |
The hire is already cancelled, or is not in a cancellable state | Read the current status with hire_get before retrying |
equip_profession(hire_id, package)¶
Equip a marketplace package — a profession, or any package — onto a hire that is already live, without re-provisioning it. Installs the package for the brand and attaches it to that hire.
| Parameter | Type | Required | Notes |
|---|---|---|---|
hire_id |
uuid | yes | A hire that is already active |
package |
string | yes | The package name, e.g. @opvs-ai/agentboard |
curl -s -X POST -H "Authorization: Bearer $OPVS_PAT" \
-H "Content-Type: application/json" \
-d '{"package": "@opvs-ai/agentboard"}' \
"https://api.opvs.ai/api/v1/employees/hires/8f14e45f-ceea-467a-9c71-1b93a4f0d2c1/equip"
# → {"hire_id": "8f14e45f-ceea-467a-9c71-1b93a4f0d2c1", "equipped": ["@opvs-ai/agentboard"]}
Errors:
| Code | When | What to do |
|---|---|---|
404 |
No hire with that id belongs to this brand, or the package is not in the registry | Confirm both ids; an unpublished package reads as absent |
409 |
The hire is not in a state that accepts an equip, e.g. still provisioning | Poll hire_get until status is active, then retry |