Authenticate an agent and list your team
How to authenticate an agent and list your team¶
Goal: give an agent a brand-pinned token so it can read your hired employees, and confirm the pin is doing its job.
This assumes you already reached the catalog with the public methods. If you have not, start with the getting-started tutorial.
Get a token¶
The CLI asks for its own token and a human approves it by email. The agent never handles your password.
npm install -g @opvs-ai/cli
opvs config set api_url https://api.opvs.ai
opvs auth request -w your-workspace-slug -e admin@example.com
# → approval email sent; polling for approval
Approve from the email and the CLI writes the token to ~/.opvs/config.json. Approval windows are
short, so click it while the command is still polling; if it expires, run the request again.
Scope the token to what the agent actually needs¶
Ask only for the scopes the work requires. An employees-reading agent does not need write access.
| Task | Scopes to request |
|---|---|
| Read the catalog | none, the methods are public |
| List the team, read a hire | employees.hire.read |
| Hire, cancel, equip | employees.hire.read, employees.hire.write |
| Read catalog profiles as a package | catalog.profile.read |
Point the MCP server at the token¶
Export the token and restart the agent host.
export OPVS_PAT="opvs_pat_2f9c41ab7e5d4c8fa1b6"
export OPVS_API_URL="https://api.opvs.ai"
opvs employees team
# → 1 hire · ai-sdr · active
Confirm the brand pin holds¶
This is the check worth doing once. The brand is resolved from the token, not from anything the agent sends, so a pinned token cannot be talked into reading another brand — including by naming a brand in the URL.
curl -s -H "Authorization: Bearer $OPVS_PAT" \
"https://api.opvs.ai/api/v1/employees/hires"
# → {"hires": [...], "total": 1} the token's own brand, always
If you hold tokens for two brands, run the same call with each and confirm you get two different lists. Two identical lists would mean the pin is not being applied, which is worth reporting.
When the team list comes back empty¶
An empty list is a real answer, not an error. Check in this order:
- The brand genuinely has no hires. Confirm in the dashboard under your team.
- The token is pinned to a different brand than you think. Re-read which workspace you approved.
- The token lacks
employees.hire.read. That returns403rather than an empty list, so if you got200with zero rows, this is not your cause.
Why hiring may return 403¶
Hiring consumes a plan seat, so it is gated on the brand having an active service plan. With no
plan attached, the call returns a structured 403 carrying limit_type, current and max
rather than a generic denial. Read limit_type to see which cap bound. Catalog reads and team
listing keep working regardless.