sharing
Manage sharing permissions (Attio-style) for tables, workflows and agents from the CLI. Use when the user asks to share a table/workflow/agent, restrict who can view or edit a resource, set workspace/
SKILL.mdskills/vendor/sharing/SKILL.md
GitHubPrerequisites
- Authenticate with
frontline auth login <api-key>. - All
sharingcommands require a USER API key. Any account user can read and change sharing (so everyone can share their own resources); aGENERALkey (no user) is rejected. - The backend always revalidates: the resource must exist and belong to your account, and every
userId/teamIdmust belong to the account.
How access works (read this first)
- Sharing is default-open: if a resource has no explicit permission rows, everyone in the account has full access (backward compatible).
- Only users with
FULL_ACCESSon a resource (or admins/owners / the resource creator) can change sharing viaset. Users withVIEWcan read the config (get) but get403onset. - The workspace level is the default for everyone. Set it to
NONEto lock the resource down so that only the listed teams/users get access. - Resolution order is user > team > workspace. Admins/owners always bypass and have full access (they can view/manage everything regardless of the rows).
- The resource creator (owner) is never restricted: whoever created the table/workflow/agent always keeps full access to it, even if the workspace is
NONEand they are not listed.
Resolve valid ids first
You must use real userId/teamId values. List them before calling set:
frontline sharing users # { results: [{ id, first_name, last_name, email }] }
frontline sharing teams # { results: [{ id, name }] }
Resource types and access levels
--entity-type is one of table, workflow, agent.
--entity-id is a positive integer for table/workflow, and a uuid string for agent.
Levels differ by resource type:
- table (aligned with workflows — three levels):
FULL_ACCESS— view + edit schema (fields, views, etc.) + full CRUD on recordsVIEW— read-only table and recordsNONE— no access
- workflow:
FULL_ACCESS— view + edit/deleteVIEW— view onlyNONE— no access
- agent (two-level model unified into one level):
FULL_ACCESS— view + edit/delete agent + intervene conversationsCAN_INTERVENE— view + intervene conversations (interrupt / send response), without editing the agentVIEW— view only (cannot intervene)NONE— no access
Intervening in conversations (interrupt, send response, cancel-interrupt) is only available from the app UI — not via Public API or CLI. The CLI
sharingcommands only manage who has permission; they do not perform intervention actions.
Sharing applies to data tables only. Standard CRM objects (people, companies, deals, tickets) are governed by record-type permissions, not this API.
Sharing is what makes a table's schema editable by a non-admin:
FULL_ACCESSis required to add, edit, or delete fields and options, andVIEWreturns403on those calls. Admins, owners, and the user who created the table always have edit access regardless of the sharing rows. Object schemas (including record types) are admin/owner-only and cannot be delegated this way — custom objects are no exception.
Get the current configuration
frontline sharing get --entity-type table --entity-id 42
frontline sharing get --entity-type workflow --entity-id 7
frontline sharing get --entity-type agent --entity-id 1f0b2c3d-....-uuid
Output:
{
"resourceType": "table",
"resourceId": 42,
"workspace": "FULL_ACCESS",
"teams": [{ "teamId": 3, "name": "Sales", "level": "VIEW" }],
"users": [
{
"userId": 5,
"firstName": "Ada",
"lastName": "Lovelace",
"email": "ada@x.com",
"level": "FULL_ACCESS"
}
],
"myLevel": "FULL_ACCESS"
}
Set the configuration (full replace)
set replaces the resource's sharing rows with what you pass. Anything you omit is cleared (workspace falls back to its default-open behavior if you don't include it).
# Lock a table to specific people/teams (workspace = NONE)
frontline sharing set --entity-type table --entity-id 42 --data '{
"workspace": "NONE",
"users": [{ "userId": 5, "level": "FULL_ACCESS" }],
"teams": [{ "teamId": 3, "level": "VIEW" }]
}'
# Workflow: everyone can view, one user can edit
frontline sharing set --entity-type workflow --entity-id 7 --data '{
"workspace": "VIEW",
"users": [{ "userId": 5, "level": "FULL_ACCESS" }]
}'
# Agent: only admins (no workspace/teams/users) — everyone else loses access
frontline sharing set --entity-type agent --entity-id 1f0b2c3d-....-uuid --data '{
"workspace": "NONE"
}'
--data keys:
workspace: a single level string (optional).teams:[{ "teamId": <number>, "level": "<level>" }](optional).users:[{ "userId": <number>, "level": "<level>" }](optional).
The CLI validates --entity-type, the id shape, and that --data is valid JSON locally. The backend validates the levels are allowed for the resource type and that the ids exist.
Common errors
bad_entity_type/bad_entity_id/bad_json: local validation failed — fix the flag.401(unauthorized): you used aGENERALkey instead of aUSERkey.400invalid ids: auserId/teamIddoes not belong to the account — re-check withsharing users/sharing teams.400invalid level: you used a workflow-only level on a table/agent, an agent-only level on a workflow/table, orCAN_EDIT_RECORDS/CAN_EDIT_TABLEon workflow/agent (legacy table levels are auto-migrated toFULL_ACCESSon PUT).403onset: caller lacksFULL_ACCESSon the resource (includes self-escalation attempts fromVIEW/CAN_INTERVENE).404onget/agents describe: caller hasNONE(resource is hidden, not just read-only).
Testing permissions (CLI)
Use two USER keys and named profiles (user-a configures, user-b is the subject). Resolve ids first:
frontline auth login <key-a> --profile user-a --base-url http://localhost:7010/public/v1
frontline auth login <key-b> --profile user-b --base-url http://localhost:7010/public/v1
frontline auth whoami --profile user-a --pretty
frontline sharing users --profile user-a --pretty
Create a disposable agent as user-a, then lock it down:
AGENT_ID=$(frontline agents create --profile user-a --name sharing-test | jq -r .id)
USER_B_ID=2
frontline sharing set --profile user-a --entity-type agent --entity-id "$AGENT_ID" \
--data "{\"workspace\":\"NONE\",\"users\":[{\"userId\":$USER_B_ID,\"level\":\"VIEW\"}]}"
Smoke checks for VIEW (user-b):
| Command | Expected |
|---|---|
sharing get |
myLevel: VIEW |
sharing set |
403 (exit 4) |
agents describe --agent-id $AGENT_ID |
200 |
agents update --agent-id $AGENT_ID --name x |
403 |
agents flows list --agent-id $AGENT_ID |
200 |
agents flows create --agent-id $AGENT_ID --name x |
403 |
Repeat after changing the level (NONE, CAN_INTERVENE, FULL_ACCESS). sharing set replaces the whole config — always include workspace plus the full users / teams arrays you want to keep.
Notes:
agents listdoes not filter by sharing; test access withdescribe/sharing getusing a known id.- Account OWNER/ADMIN bypass sharing rows; use a plain USER as the subject.
- The resource creator always keeps
FULL_ACCESS, even whenworkspace: NONEand they are not listed. - CLI exit codes:
0ok,3= 404,4= 401/403.
See also
- Full reference: https://docs.getfrontline.ai/docs (concepts) and https://docs.getfrontline.ai/cli (command reference).
- App UI walkthroughs: https://help.getfrontline.ai.