VitrinaAPI

Automate replies and assignment

Saved replies an agent applies in one click, and the routing that places conversations.

You fire a macro yourself, from the inbox. An assignment rule runs on its own, with nobody calling it, every time a new conversation arrives.

Macros

A macro holds customer-facing text (content, or a send_message step) and an ordered list of actions. That list takes notes, labels, status changes, snoozing, assigning an agent or a team, and moving a lead's stage. A failing step is recorded and the rest still run. So a 200 with failures inside it is normal.

Reading needs macros:read; CRUD needs macros:write. Running one also needs only macros:read, even though it sends the customer a message and changes ticket state. Keep that in mind when you hand out that scope.

curl -X POST https://api.vitrinadev.com/api/v1/macros \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Grateful close",
    "content": "Thanks for reaching out, [[name]]. Anything else, we are here.",
    "actions": [{ "type": "change_status", "value": "resolved" }],
    "params": [{ "key": "name", "label": "Customer name", "input_type": "text" }]
  }'
{
  "data": {
    "id": "d3d3d3d3-0000-4000-8000-000000000001",
    "tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
    "name": "Grateful close",
    "content": "Thanks for reaching out, [[name]]. Anything else, we are here.",
    "actions": [{ "id": "a1", "type": "change_status", "value": "resolved" }],
    "params": [{ "key": "name", "label": "Customer name", "input_type": "text" }],
    "active": true,
    "usage_count": 0,
    "created_at": "2026-09-22T13:00:00.000Z",
    "updated_at": "2026-09-22T13:00:00.000Z"
  }
}

Templates carry two kinds of placeholder. {{auto}} resolves on its own from the conversation; [[manual]] is what the agent fills in.

The reviewed flow is two calls. GET /macros/{id}/prepare builds the draft and sends nothing, then POST /macros/{id}/run sends the text the agent approved and runs the steps. POST /macros/{id}/apply is the direct version, with no review, meant for macros without manual params and for automation.

Voice clips

A voice clip is a recorded voice note the workspace keeps for reuse: the team sends it from the inbox and the AI agent sends it in a conversation. Each clip has a name, tags, a «cuándo usarlo» (when to use it) note, an optional expiry and an optional model filter (for example, only iPhone 13 and later). It is transcribed on upload, and the transcript can be corrected.

An expired clip stays in the library, marked expired, but is never offered for sending. GET /voice-clips?live=true&model=... returns only the clips that may be sent now for that model. It uses the same permissions as macros: macros:read to see and play, macros:write to upload, edit and delete.

Assignment rules

Routing is ordered and first match wins. Each rule filters on channel (match_channels) and, if you want, on origin (match_sources). It points at a destination: a team or a list of agents. The winner is the first rule, in priority order, whose filter matches and whose destination isn't empty.

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

curl -X POST https://api.vitrinadev.com/api/v1/assignment-rules \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "WhatsApp → support team",
    "match_channels": ["whatsapp"],
    "team_id": "cccccccc-0000-4000-8000-000000000001"
  }'
{
  "data": {
    "id": "d5d5d5d5-0000-4000-8000-000000000001",
    "tenant_id": "a1a1a1a1-0000-4000-8000-000000000001",
    "name": "WhatsApp → support team",
    "priority": 0,
    "enabled": true,
    "match_sources": null,
    "match_channels": ["whatsapp"],
    "team_id": "cccccccc-0000-4000-8000-000000000001",
    "assignee_user_ids": null,
    "assignment_mode": "auto_round_robin",
    "is_portal_default": false,
    "created_at": "2026-09-22T13:00:00.000Z",
    "updated_at": "2026-09-22T13:00:00.000Z"
  }
}

A new rule always appends last; sending your own priority is refused. To reorder, call PUT /assignment-rules/order with every id in the new order. It applies all or nothing.

Removing a member with DELETE /memberships/{id} (Team) strips their id from every rule and every channel rotation. Assignment only deals to active members. If you keep your own copy of assignee_user_ids, it can go stale: read it again before you use it.

The full contracts live in the Macros and Assignment rules reference.

On this page