Roles and permissions
Destesi has three layers of permission. Each answers one question and has one owner, and keeping them apart is what keeps the whole thing simple.
| Layer | Question it answers | Who defines it |
|---|---|---|
| 1. Workspace role + product access | Who administers the workspace? Which products may someone open? | Destesi: owner, admin, member |
| 2. Product roles | What may someone do inside a product? | Your company: named sets of a product’s permissions |
| 3. Data scope | Which stage, which stock owner? | The product itself |
Layer 1 — the workspace role
Section titled “Layer 1 — the workspace role”Everyone you invite to a workspace holds exactly one of three roles.
| Role | What it may do |
|---|---|
| owner | Everything an admin may do, plus billing and plan changes, and deleting the workspace. |
| admin | Invite, remove and re-role members; set which products a member may enter and which product roles they hold; manage product roles; change a product’s settings where the product gates them. |
| member | Use the products, within the product roles they hold. No workspace administration. |
The rules that follow from the table hold everywhere:
- Billing is owner-only. An admin sees the plan and what it costs but cannot change it.
- Administration is admin-or-owner. Members, invites, product access and
product roles refuse a member with
admin_requiredeverywhere, and the screen tells them to ask an admin. Every product gates its own settings the same way: a member reads a product’s configuration and cannot save it. The only exceptions are the three products that have no settings to gate — Chat, Text-to-Speech and Image generation. - Some verbs are owner-only. An admin may set a member to
adminor back tomember, but only an owner may hand out ownership, delete the workspace, or change who pays for it. Those refuse an admin withowner_required— notadmin_required, which would tell an admin to become the thing they already are. - A workspace always has an owner. Demoting or removing the last owner is
refused (
last_owner). Promote someone else first.
An invitation can be sent as member or admin. Making someone an owner
is a change you make after they have joined — and one only another owner can
make.
Product access — which products a member may enter
Section titled “Product access — which products a member may enter”Every member can enter every product by default. An owner or admin can narrow that to a list: this person may use Drive and Commerce, and nothing else.
An owner may not be restricted (owner_must_be_unrestricted), and an
unknown product id is rejected rather than accepted — a typo that quietly meant
“allowed nowhere” would look like a bug rather than a setting.
Layer 2 — product roles
Section titled “Layer 2 — product roles”A permission is a capability a product declares, such as orders.manage
in Commerce or receive in Inventory. The list is fixed by the product and
uses plain verbs, not industry words.
A product role is your company’s own name — “Vendedor”, “Empacador”, “Bodega” — for a set of one product’s permissions. Roles are data in your workspace: you create, rename and delete them. “admin” is never one of them, because administration is layer 1.
Each member holds, per product, a list of roles:
- No roles set for a product → full member access there. This is everyone until you assign a role, so turning product roles on locks nobody out.
- One or more roles → only the union of those roles’ permissions.
- An empty list → read-only in that product.
- Owners and admins bypass product roles entirely. An owner never holds
them (
owner_must_be_unrestricted). - Deleting a role removes it from every member who held it, and they stay restricted in that product — possibly read-only. A delete narrows access; it never widens it.
- Product access still comes first. Roles in a product the member cannot enter do nothing.
Reading is open to anyone who may enter the product, except the reads a product ties to a permission — customer conversations and money in Commerce. A product’s settings, integrations and locations stay layer 1: no product role can grant them.
A verb the member’s roles do not cover refuses with permission_required, and
the response names the missing permission.
Commerce permissions
Section titled “Commerce permissions”| Permission | What it allows |
|---|---|
products.manage |
Edit products, categories, care templates, bulk edits, extract catalog PDFs. |
orders.manage |
Create, confirm, cancel and settle orders; payment links; addresses; imports. |
shipping.manage |
Quote shipping, packages, pickups and pickup manifests, mark shipped/delivered/returned, carrier incidents, receive returns. |
labels.buy |
Buy, protect, void and reship labels, and buy return labels: spends carrier wallet money. |
conversations.reply |
Read customer conversations and take them over, send WhatsApp messages, follow-up drafts. |
campaigns.manage |
Campaign drafts and groups, audiences, content sets, image jobs, organic posts; publish (always paused) and pause. |
campaigns.spend |
Turn a published ad on and change a live budget: spends ad money. |
strategies.manage |
Turn automated strategies on or off. |
finance.manage |
See the wallet and top it up. |
Inventory permissions
Section titled “Inventory permissions”Inventory’s permissions are the same operation names its integration credentials use, so people and connected systems share one vocabulary.
| Permission | What it allows |
|---|---|
items |
Create, edit and import items. |
receive |
Receive stock into a location. |
issue |
Issue stock out of a location. |
transfer |
Move stock between locations. |
adjust |
Adjust stock counts to match reality. |
reserve |
Reserve and release stock for demand. |
fulfill |
Allocate and fulfill reserved stock. |
dispatch |
Hand a prepared order to the carrier or customer. |
reverse |
Reverse a recorded stock movement. |
owner |
Change the owner of stock. |
condition |
Change the condition of stock. |
A member whose role holds one of these may run that operation even where a
member without roles may not (adjust, reverse, items, owner,
condition). Inventory’s configuration stays admin-only.
Other products declare no permissions, so they have no product roles: Relay uses its stage roles (below), and Drive, Text-to-Speech and Image generation have nothing inside to restrict.
Layer 3 — data scope
Section titled “Layer 3 — data scope”Some products narrow which records someone may act on, and that stays inside the product.
Relay ships no roles at all: a role is whatever you call the people who do a step, and writing it on a stage in your process is what creates it. Give a person that role and they may move the stages that carry it. A stage with no role is open to anyone in the workspace, and owners and admins may act on any stage.
Inventory binds each integration credential to the operations and the stock owners it may touch, so a connected system only moves the stock it is meant to.
Changing all of this
Section titled “Changing all of this”account.destesi.io → Workspace is the home for membership. As an owner or admin you get, on the active workspace:
- the member list with a role dropdown (owner, admin, member) on each row,
- a product access button beside it — it reads “All products” or the count, and opens a checkbox per product the workspace can use, with All products to lift the restriction,
- a roles control on each member, to pick their roles per product; owners and admins read “Everything”, because roles do not apply to them,
- a Roles section listing each product’s roles, where you create one — optionally starting from a template — edit its permissions or delete it,
- an invite form with a role of
memberoradminand an optional Limit access for the products they can open and their roles, plus the pending invites with a Revoke next to each, - Remove on a member.
Members see the same page read-only. An owner’s row shows “All products” locked, because an owner cannot be restricted.
dst workspace members # emails, user ids and rolesdst workspace invite ana@example.com --role admindst workspace role u_… admin # owner | admin | memberdst workspace access u_… --products drive,commerce # only these productsdst workspace access u_… --all # lift the restrictiondst workspace remove u_…
dst workspace permissions --product commerce # what a product role can grantdst workspace roles --product commerce # the workspace's roles, with idsdst workspace roles create --product commerce --name "Vendedor" \ --permissions orders.manage,conversations.replydst workspace roles update <role-id> --permissions orders.managedst workspace roles delete <role-id>dst workspace member-roles u_… --product commerce --roles <role-id>,<role-id>dst workspace member-roles u_… --product commerce --none # read-onlydst workspace member-roles u_… --product commerce --unrestricted # full member accessEvery one takes --workspace <slug> and otherwise uses the active workspace
(dst workspace switch). --permissions "" creates or leaves a read-only
role; member-roles changes one product and leaves the others as they are.
An agent connected to Destesi’s MCP server has the same verbs, authenticated as you with your own token — so it can do exactly what you can do and nothing more:
| Tool | What it does |
|---|---|
workspace_members |
The people in a workspace with their roles and user ids |
workspace_invite |
Invite by email as member or admin, optionally with product_access and product_roles |
workspace_set_role |
Set a member’s role to owner, admin or member (owner needs an owner) |
workspace_set_product_access |
Restrict a member to some products, or lift it |
workspace_product_permissions |
The permissions each product’s roles can grant |
workspace_product_roles |
The workspace’s product roles, with ids |
workspace_create_product_role |
Create a role: a product, a name and its permissions |
workspace_update_product_role |
Rename a role or replace its permissions |
workspace_delete_product_role |
Delete a role |
workspace_set_member_product_roles |
Set a member’s roles in one product: a list, [] for read-only, or unrestricted |
Ask in plain words — “make Priya an admin”, “give Priya only Drive”, “create a
Vendedor role in Commerce that can manage orders and reply to customers” — and
the agent picks the tool. If you are a member, the write refuses with
admin_required and the agent tells you so.
Destesi’s own chat assistant has the four membership tools —
workspace_members, workspace_invite, workspace_set_role and
workspace_set_product_access — acting as you with your Destesi session rather
than a token. Ask it “who is in this workspace?” or “give Priya only Drive” and
it does the same thing as the other surfaces. The three writes pause for your
approval before they apply, and you can edit the draft first. A scheduled
routine never gets them: it runs unattended, with nobody’s session behind it.
The assistant does not manage product roles; use the account web, dst or
MCP for those.
The refusals
Section titled “The refusals”You will meet exactly five “you are signed in and the answer is still no” messages anywhere in Destesi.
| Code | Meaning | What to do |
|---|---|---|
admin_required |
The verb shapes the workspace and you are a member. | Ask an owner or admin. |
owner_required |
The verb is owner-only: billing, deleting the workspace, and promoting someone to owner. | Ask the workspace owner. |
product_forbidden |
Your product access does not include this product. | Ask an owner or admin to widen it. |
permission_required |
Your product roles in this product do not include the permission the verb needs; the response names it in permission. |
Ask an owner or admin to add it to one of your roles. |
forbidden_scope |
Your personal access token is read-only for this call. | Mint a token with a higher scope. |
Managing product roles has its own validation errors: unknown_product,
unknown_permission, invalid_name and role_name_taken when creating or
editing a role, and unknown_role when assigning one that does not exist for
that product.
Credits
Section titled “Credits”Every metered action in the suite — an assistant turn, an image, a code review, a render minute — draws from one credit wallet per workspace. 1 credit = $0.01. The wallet has two pools: included credits come with the plan and reset on the first of each month, no rollover; granted credits come from packs, last 12 months and are spent after the monthly pool. A failed generation is never charged — credits are reserved when an action is admitted and settled only on success.
The wallet reads the same on the three agent surfaces; any member may read it.
dst workspace credits # this month's wallet and the rate carddst workspace credits --json # the raw walletdst workspace credits --workspace acme-coworkspace_credits takes the workspace_id (from workspace_list) and
returns the wallet as JSON.
Ask “how many credits do we have left?” — the assistant calls
workspace_credits as you, on the active workspace.
All three return the same shape:
| Field | Meaning |
|---|---|
period |
The month the wallet is for, YYYY-MM. |
mode |
record — usage is metered but nothing is refused; enforce — actions refuse with quota_exceeded at zero. |
included / granted |
Plan credits for the month / pack credits still valid. |
used / reserved |
Settled this month / held by actions still running (released if they fail). |
remaining |
What is left to spend: included + granted − used − reserved. |
rates[] |
The rate card: credits per per unit of each action (for example 25 credits per 1 turn, 1 credit per 1000 characters). |
Until the wallet route is deployed on your identity service the three surfaces answer “credits are not available on this identity yet” — nothing is metered and nothing is refused.