VitrinaAPI

Manage the workspace's people

Invite people, move them between roles or branches, and remove them cleanly.

GET /memberships lists the people in the workspace. Every member carries a base role (owner, admin, supervisor, agent, consultant), and a custom_role_id can replace it outright. When that field has a value, the member's effective scopes are exactly the custom role's.

location_ids says which branches a member is posted to. It only matters when their role carries stock_visibility: own_locations.

Reading needs memberships:read; writing, memberships:write.

Trap

You can't grant a rank above your own

Changing a member's role or branch goes through an escalation ceiling. You never grant a rank higher than yours, and you never hand out branches you can't see. An API key works the same way: a connected app doesn't end up with more authority than the member who connected it.

See the roster

curl https://api.vitrinadev.com/api/v1/memberships \
  -H "Authorization: Bearer $VITRINA_KEY"
{
  "data": [
    {
      "id": "11111111-0000-4000-8000-000000000001",
      "tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
      "user_id": "11111111-0000-4000-8000-000000000001",
      "role": "agent",
      "custom_role_id": "d2d2d2d2-0000-4000-8000-000000000001",
      "custom_role_name": "Vendedor sucursal",
      "status": "active",
      "display_name": "Camila Rojas",
      "phone": "+56912345001",
      "avatar_url": null,
      "location_ids": ["b1b1b1b1-0000-4000-8000-000000000001"],
      "created_at": "2026-01-10T13:00:00.000Z",
      "updated_at": "2026-09-10T13:00:00.000Z"
    }
  ],
  "meta": { "total": 1 }
}

Invite by email

Most people join by invitation rather than by an id you already know:

curl -X POST https://api.vitrinadev.com/api/v1/memberships/invitations \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "[email protected]",
    "role": "agent",
    "custom_role_id": "d2d2d2d2-0000-4000-8000-000000000001",
    "location_ids": ["b1b1b1b1-0000-4000-8000-000000000001"]
  }'

The response carries accept_url, the sign-up link. It's what you use while the workspace has no mail provider configured:

{
  "data": {
    "id": "e5e5e5e5-0000-4000-8000-000000000001",
    "email": "[email protected]",
    "role": "agent",
    "custom_role_id": "d2d2d2d2-0000-4000-8000-000000000001",
    "custom_role_name": "Vendedor sucursal",
    "location_ids": ["b1b1b1b1-0000-4000-8000-000000000001"],
    "accept_url": "https://app.vitrinadev.com/invite/a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4",
    "expires_at": "2026-09-17T13:00:00.000Z"
  }
}

role and custom_role_id are independent, same as adding a member directly. role sets the escalation rank; the custom role decides effective access. location_ids rides along with the invitation and lands on the membership the moment the person accepts. A branch-scoped seat is already narrowed at the first login.

An API key can't accept an invitation

POST /memberships/invitations/by-token/{token}/accept needs no scope. Its boundary is the token plus an exact email match. Only a person can call it: a user session, or a personal token acting as that person (see Personal tokens). An API key gets 403. Accepting twice with the same token returns the same membership and applies nothing a second time.

Change a role, status or branch

The PUT is partial: send only what changes.

curl -X PUT https://api.vitrinadev.com/api/v1/memberships/11111111-0000-4000-8000-000000000002 \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "custom_role_id": "d2d2d2d2-0000-4000-8000-000000000001", "location_ids": ["b1b1b1b1-0000-4000-8000-000000000001"] }'

display_name, phone and avatar_url ride on the same PUT and write the target member's profile. Those three skip the self-action restriction that role and status carry, so editing your own identity is always allowed. To upload a photo as a file instead of passing a URL, use POST /memberships/{id}/avatar (multipart, up to 5MB).

Remove someone without stranding their work

curl -X DELETE "https://api.vitrinadev.com/api/v1/memberships/11111111-0000-4000-8000-000000000002?handover=round_robin" \
  -H "Authorization: Bearer $VITRINA_KEY"
{
  "data": {
    "mode": "round_robin",
    "conversationsMoved": 6,
    "leadsMoved": 3,
    "perAssignee": { "11111111-0000-4000-8000-000000000003": 9 }
  }
}

handover decides where the person's open work goes.

handoverWhat happens to the open work
unassign (default)Conversations surface as unassigned and nobody is notified.
userEverything moves to the inheritor you name in handover_assignee_id.
round_robinIt's dealt across the rest of the team.

Before you remove anyone, GET /memberships/{id}/handover-preview shows how much open work they hold and who could inherit it.

Field by field, memberships and invitations are in the Team reference.

On this page