Workspace settings

The calls behind Settings in the dashboard: which optional features a workspace uses, the people in it, your own OPVS address, and updates for your machines. The dashboard uses the same routes. For what each settings page does, see Settings: where each setting lives.

Before you call

Every route takes a personal access token, Authorization: Bearer $OPVS_PAT, and acts on the workspace the token belongs to. {brand_id} in a path must be that same workspace.

Group Who may call Token scope
Read feature switches any active member brand:read
Change a feature switch, members and guests a workspace admin brand:admin
Your OPVS address the token's own person any
Machine updates a workspace admin any

brand:read is in every workspace's default scope ceiling. brand:admin is not: a platform admin has to widen your workspace's policy first (see Authentication). Without it, make these changes in the dashboard.

A token that lacks a route's scope gets 403 with a detail that names the missing scope and says whether your workspace allows it.

Feature switches

A workspace has four optional features: skills, feed, machines and crm. Turning one off hides it from the dashboard and deletes nothing. See Turn a feature on or off.

GET /api/v1/brands/{brand_id}/features

Returns every switch with its current state and what the feature holds.

curl "https://api.opvs.ai/api/v1/brands/1/features" \
  -H "Authorization: Bearer $OPVS_PAT"

# Response 200, application/json (two of the four switches shown)
# {"new_navigation": true,
#  "switches": {
#    "skills": {"on": true, "locked_by_plan": false, "source": "data_default",
#               "changed_by": null, "changed_at": null,
#               "holds": {"installed_packages": 59}},
#    "feed":   {"on": true, "locked_by_plan": false, "source": "data_default",
#               "changed_by": null, "changed_at": null,
#               "holds": {"feed_sources": 8}}}}

# Without a token the route returns a 401 error.

Add ?format=yaml or ?format=md for a smaller answer.

Response

Field Type Meaning
new_navigation boolean Whether the workspace uses the current sidebar and Settings
switches.<key>.on boolean Whether the feature is on
switches.<key>.locked_by_plan boolean true when the plan does not include the feature; it cannot be turned on
switches.<key>.source string Why it has this value: plan_default, data_default (the workspace already had data for it) or admin (an admin set it)
switches.<key>.changed_by object or null {user_id, name} of the admin who last set it
switches.<key>.changed_at string or null When an admin last set it
switches.<key>.holds object Counts of what the feature holds, such as installed_packages, feed_sources, machines or crm_boards

Errors

Status When Resolution
401 no token, or the token is not valid send Authorization: Bearer $OPVS_PAT with a live token
403 the token lacks brand:read request a token with brand:read
404 Brand not found {brand_id} is not a workspace you belong to use the token's own workspace id
422 format is not yaml or md drop format, or use one of the two values

PUT /api/v1/brands/{brand_id}/features/{key}

Turns one feature on or off for everyone in the workspace, and returns every switch as it now stands. {key} is skills, feed, machines or crm.

curl -X PUT "https://api.opvs.ai/api/v1/brands/1/features/feed" \
  -H "Authorization: Bearer $OPVS_PAT" \
  -H "Content-Type: application/json" \
  -d '{"on": false}'

# Response 200: the same shape as GET /features, with
#   switches.feed.on = false, source = "admin" and changed_by naming you.
# A feature your plan does not include returns a 409 error when you turn it on.

Request body: on (boolean, required).

Errors

Status When Resolution
401 no token, or the token is not valid send a live token
403 the token lacks brand:admin change the switch in the dashboard, or have your workspace's policy widened to allow brand:admin
403 Brand admin access required you are not an admin of this workspace ask an admin
404 Brand not found {brand_id} is not a workspace you belong to use the token's own workspace id
409 This feature is not included in the workspace plan turning on a feature with locked_by_plan: true change plan under Billing & plans first
422 {key} is not one of the four, or on is missing send one of the four keys and {"on": true} or {"on": false}

Members

Every route in this group needs brand:admin and an admin of the workspace. {user_id} must be an active member.

PATCH /api/v1/brands/{brand_id}/members/{user_id}

Changes a person's role or position in the workspace.

curl -X PATCH "https://api.opvs.ai/api/v1/brands/1/members/482" \
  -H "Authorization: Bearer $OPVS_PAT" \
  -H "Content-Type: application/json" \
  -d '{"role": "admin", "position": "Head of Sales"}'

# Response 200
# {"success": true}

# Making the only admin a member returns a 409 error.

Request body: role (string, optional): admin or member. position (string, optional): free text; send null to clear it. Send at least one.

Errors

Status When Resolution
400 Role must be 'admin' or 'member' another role value send admin or member
400 No fields to update the body has neither field send role, position or both
403 the token lacks brand:admin, or you are not an admin use an admin's brand:admin token
404 Member not found the person is not an active member check {user_id} against Members
409 The last workspace admin cannot be changed to a member demoting the only admin make someone else an admin first

POST /api/v1/brands/{brand_id}/members/{user_id}/photo

Uploads a picture for a member. Send the file as multipart field file: PNG, JPG or GIF, up to 5 MB.

curl -X POST "https://api.opvs.ai/api/v1/brands/1/members/482/photo" \
  -H "Authorization: Bearer $OPVS_PAT" \
  -F "file=@portrait.png"

# Response 200
# {"photo": "https://files.opvs.ai/…/portrait.png"}

# A file over 5 MB returns a 400 error.

Errors

Status When Resolution
400 File must be PNG, JPG or GIF another file type convert the image
400 File too large (max 5MB) the file is over 5 MB resize it
403 the token lacks brand:admin, or you are not an admin use an admin's brand:admin token
404 Member not found the person is not an active member check {user_id}
503 Image storage not configured picture storage is unavailable try again later

DELETE /api/v1/brands/{brand_id}/members/{user_id}/photo

Removes a member's picture.

curl -X DELETE "https://api.opvs.ai/api/v1/brands/1/members/482/photo" \
  -H "Authorization: Bearer $OPVS_PAT"

# Response 200
# {"photo": null}

# A person who is not an active member returns a 404 error.

Errors

Status When Resolution
403 the token lacks brand:admin, or you are not an admin use an admin's brand:admin token
404 Member not found the person is not an active member check {user_id}

POST /api/v1/brands/{brand_id}/members/{user_id}/reset-password

Emails the member a link to choose a new password.

curl -X POST "https://api.opvs.ai/api/v1/brands/1/members/482/reset-password" \
  -H "Authorization: Bearer $OPVS_PAT"

# Response 200
# {"success": true, "message": "Password reset email sent to ada@example.com"}

# If the email cannot be sent the route returns a 502 error.

Errors

Status When Resolution
403 the token lacks brand:admin, or you are not an admin use an admin's brand:admin token
404 Member not found the person is not an active member check {user_id}
502 Failed to send password reset email the email could not be sent try again later

POST /api/v1/brands/{brand_id}/members/{user_id}/set-password

Sets a member's password directly. This works only for a person who belongs to this workspace and no other; for everyone else, send a reset link.

curl -X POST "https://api.opvs.ai/api/v1/brands/1/members/482/set-password" \
  -H "Authorization: Bearer $OPVS_PAT" \
  -H "Content-Type: application/json" \
  -d '{"password": "correct-horse-battery", "send_email": true}'

# Response 200
# {"success": true, "message": "Password updated for ada@example.com"}

# A person who belongs to another workspace too returns a 409 error.

Request body: password (string, required, at least 8 characters). send_email (boolean, optional, default false): also email the person that their password changed.

Errors

Status When Resolution
400 User has no auth account the person has never signed in with a password invite them, or send a reset link
403 the token lacks brand:admin, or you are not an admin use an admin's brand:admin token
404 Member not found the person is not an active member check {user_id}
409 Send a reset link instead of setting this person's password. the person is in more than one workspace, or is an OPVS staff or service account use reset-password
422 password is shorter than 8 characters send a longer password
502 Failed to set password the sign-in service did not accept the change try again later

Guests

A guest is someone outside the workspace who can open some of its boards or docs. Every route here needs brand:admin and an admin of the workspace.

GET /api/v1/brands/{brand_id}/guests

Lists every guest and each board or docs space they can open.

curl "https://api.opvs.ai/api/v1/brands/1/guests" \
  -H "Authorization: Bearer $OPVS_PAT"

# Response 200
# {"guests": [{"user_id": 913, "email": "sam@example.com", "full_name": "Sam Reyes",
#              "image": null,
#              "access": [{"kind": "board", "resource_id": "6f1c2a7e-3b0d-4c55-9e1a-2d8f4b7c9a10",
#                          "name": "Launch plan", "role": "editor"}]}],
#  "total": 1}

# Without a token the route returns a 401 error.

Response: guests[].access[].kind is board or docs; role is the guest's level on that board or space, or null.

Errors

Status When Resolution
401 no token, or the token is not valid send a live token
403 the token lacks brand:admin, or you are not an admin use an admin's brand:admin token
502 AgentBoard returned an invalid response the boards service answered badly try again later

POST /api/v1/brands/{brand_id}/guests/{user_id}/make-member

Invites a guest to join the workspace as a member. Their guest access stays until they accept.

curl -X POST "https://api.opvs.ai/api/v1/brands/1/guests/913/make-member" \
  -H "Authorization: Bearer $OPVS_PAT"

# Response 201
# {"id": 2207, "email": "sam@example.com", "status": "pending",
#  "expires_at": "2026-10-22T16:00:00Z", "created_at": "2026-10-15T16:00:00Z"}

# A guest who already has a pending invitation returns a 400 error.

Errors

Status When Resolution
400 User is already a member of this brand the person is already a member nothing to do
400 An invitation is already pending for this email an invitation is waiting wait for them to accept; Settings → Members → Invitations lists it
403 the token lacks brand:admin, or you are not an admin use an admin's brand:admin token
404 Guest not found the person is not a guest of this workspace list guests first

DELETE /api/v1/brands/{brand_id}/guests/{user_id}

Removes a guest from every board and docs space in the workspace. Cards, comments and pages they wrote stay.

curl -X DELETE "https://api.opvs.ai/api/v1/brands/1/guests/913" \
  -H "Authorization: Bearer $OPVS_PAT"

# Response 200
# {"success": true, "boards_removed": 2, "docs_removed": 1}

# A person who is not a guest returns a 404 error.

Errors

Status When Resolution
403 the token lacks brand:admin, or you are not an admin use an admin's brand:admin token
404 Guest not found the person is not a guest of this workspace list guests first
502 AgentBoard returned an invalid response the boards service answered badly try again later

Your OPVS address

Every person gets an @opvs.run address when they sign up. These routes act on the token's own person.

GET /api/v1/me/mail/address/availability

Checks whether a new name for your address is free. name is 3 to 30 lowercase letters, numbers or hyphens, starting and ending with a letter or number.

curl "https://api.opvs.ai/api/v1/me/mail/address/availability?name=ada-lovelace" \
  -H "Authorization: Bearer $OPVS_PAT"

# Response 200
# {"name": "ada-lovelace", "domain": "opvs.run", "available": true}

# A name shorter than 3 characters returns a 422 error.

Errors

Status When Resolution
401 no token, or the token is not valid send a live token
404 Not found the token's person has no OPVS address none to change
422 name breaks the naming rule use 3 to 30 lowercase letters, numbers or hyphens

PUT /api/v1/me/mail/address

Changes your address. Mail to the old address is forwarded to the new one for 30 days.

curl -X PUT "https://api.opvs.ai/api/v1/me/mail/address" \
  -H "Authorization: Bearer $OPVS_PAT" \
  -H "Content-Type: application/json" \
  -d '{"name": "ada-lovelace"}'

# Response 200
# {"address": "ada-lovelace@opvs.run",
#  "forwarding_from": "ada.l@opvs.run",
#  "forwards_until": "2026-11-14T16:00:00Z"}

# A name someone else holds returns a 409 error.

Errors

Status When Resolution
404 Not found the token's person has no OPVS address none to change
409 This OPVS address is not available the name is taken, or is still forwarding for someone else check availability first and pick another name
409 This is already your OPVS address the name is your current one nothing to do
409 This mailbox cannot be renamed the address is not on opvs.run it cannot be changed
422 name breaks the naming rule use 3 to 30 lowercase letters, numbers or hyphens

Machine updates

Ask OPVS to update the software on your machines. A request is queued, never run at once: OPVS updates each machine when no build is running on it. Both routes need an admin of the workspace.

POST /api/v1/machines/{machine_id}/updates

Requests an update for one machine.

curl -X POST "https://api.opvs.ai/api/v1/machines/8d2e6b1a-4f3c-4e9a-b7d5-1c0f9a2e3b44/updates" \
  -H "Authorization: Bearer $OPVS_PAT"

# Response 202
# {"id": "c1a9e7f0-2b6d-4d38-8e51-7a4b3c2d1e90",
#  "machine_id": "8d2e6b1a-4f3c-4e9a-b7d5-1c0f9a2e3b44", "machine_name": "build-01",
#  "status": "queued", "requested_by": {"user_id": 482, "name": "Ada Lovelace"},
#  "requested_at": "2026-10-15T16:00:00Z", "wait_for_idle": true, "error": null}

# A machine that is not yours returns a 404 error.

Errors

Status When Resolution
403 you are not an admin of this workspace ask an admin
404 Not found no machine with that id in your workspace check the id under Settings → Machines
409 This machine shares its sidecar installation with another brand and remains operator-managed. the machine is shared with another workspace OPVS updates it for you; no request is needed

POST /api/v1/machines/updates

Requests an update for every machine in the workspace. Machines that cannot be requested come back with status: "failed" and an error; the rest are queued.

curl -X POST "https://api.opvs.ai/api/v1/machines/updates" \
  -H "Authorization: Bearer $OPVS_PAT"

# Response 202 (abridged: one machine, four of its fields)
# {"requests": [{"machine_id": "8d2e6b1a-4f3c-4e9a-b7d5-1c0f9a2e3b44", "machine_name": "build-01",
#                "status": "queued", "error": null}],
#  "queued": 1, "failed": 0, "requested_at": "2026-10-15T16:00:00Z"}

# Without a token the route returns a 401 error.

Errors

Status When Resolution
401 no token, or the token is not valid send a live token
403 you are not an admin of this workspace ask an admin
Powered by OPVS
Workspace settings | OPVS