Share a toolbox
/toolbox/{id}/share
Grants one user, team, or the whole org access at a level. Levels are ordered `view` < `use` < `edit`: `use` DELEGATES — the grantee executes tools on this toolbox, running each pinned entry through ITS delegator's own authority, never their own. Re-granting the same grantee replaces the level rather than stacking. The response repeats the toolbox's `delegation` summary — the same bounded rollup `GET /toolbox/{id}` carries, so a client that shares without ever having read the detail still learns what it just handed out. Rejects a dynamic id with 400 — there is no ACL behind one. Requires `edit` access or ownership, and `toolbox:share`.
Path Parameters
Stored toolbox id (tbx_…).
Request Body
User id (usr_…) or team id (team_…). Omit or send null for grantee_type: "org".
user and team grants target one grantee_id; org applies to every member of the organization and takes no id.
userteamorg
Same ladder for every shareable resource, low to high. view — see it exists, read its metadata/config; cannot exercise it. use — view + exercise it, resource-specific: run tools through a connection; execute a toolbox's tools through its bound connections (this DELEGATES — the caller runs through each entry's pinning editor's own authority, not necessarily their own); stamp a new toolbox by copying a template's entries; create connections from a connector. edit — use + change its settings, entries and its own grants.
viewuseedit
Response Body
What this toolbox RUNS ON — the disclosure side of §6 delegation. Sharing a toolbox at use hands the grantee execution over every connection its entries reach (pinned directly, or reached through a synthetic entry's steps, where the synthetic tool's owner is the delegator), while leaving the connections themselves invisible to them. A bounded rollup, never the set: a toolbox pinning a thousand entries across hundreds of connections returns the same size response. Present on every stored-toolbox response — detail, create, update and share — and always empty for a dynamic row, which delegates nothing. On a create/update/share response it may instead be null: the write succeeded and the summary alone could not be computed (never "nothing is delegated", which is an all-zeroes summary) — re-read GET /toolbox/{id} for the disclosure.
Distinct connections the tools run through, whether or not the caller can see them.
Distinct people whose access those pins ride on. Never named at this level.
The same delegation restricted to what THIS caller may already see (§4). Every label and delegator name lives in here, so a use grantee is never told the name or owner of a connection GET /connection/{id} would 404 for them.
3 properties
At most 3 of the visible connections, private ones first.
10 properties
May the caller grant someone access to this connection directly — the same derivation GET /connection/{id}'s can_share and POST /connection/{id}/share's own gate use.
Whose access this delegation rides on. name/email are absent for a user this org can no longer resolve. Read it together with delegated_by_source.
3 properties
HOW delegated_by was entitled to this connection — accessVia over the DELEGATOR, with §8's own precedence: the BROADEST true source wins (owner, else org, else team, else direct) and the access level plays no part. delegated_by alone only says who pinned it; without this, copy reads an org-wide or team grant as a personal favour and hides how far the account actually reaches. Null whenever the delegation no longer works — the delegator is no longer an active member, or their reach over the connection has fallen below use — since the only honest statement left is who pinned it. Same predicate as the entry's own field (delegationStanding), so the two surfaces cannot disagree.
ownerdirectteamorgnull
The team behind delegated_by_access_via: 'team', present ONLY when the CALLER is themselves a member of it — §8's bound verbatim, since a third party's team is a grantee of a connection the caller may not be able to open, and the grantee list stays behind can_see_shares. Absent otherwise; the copy then says "a team" without naming it.
2 properties
delegator = the pin's own stamped delegator (or, for via: synthetic_tool, the tool's owner): this person really is who the execution rides on. connection_owner = the pin carries no delegator (an entry written before the column existed, never backfilled), so delegated_by is the connection's OWNER standing in as the delegator of record. Such a pin is still a real delegation and is still counted; copy that names a person must branch on this rather than claim they shared it.
delegatorconnection_owner
entry = an entry pins it directly. synthetic_tool = reached only through a synthetic tool's steps.
entrysynthetic_tool
Of the visible ones, how many carry no grants at all — only their owner can reach them today.
User id (usr_…) or team id (team_…). Null for a grantee_type: "org" grant.
user and team grants target one grantee_id; org applies to every member of the organization and takes no id.
userteamorg
ACL entry id — the :aclId a DELETE .../share/{aclId} call takes.
Same ladder for every shareable resource, low to high. view — see it exists, read its metadata/config; cannot exercise it. use — view + exercise it, resource-specific: run tools through a connection; execute a toolbox's tools through its bound connections (this DELEGATES — the caller runs through each entry's pinning editor's own authority, not necessarily their own); stamp a new toolbox by copying a template's entries; create connections from a connector. edit — use + change its settings, entries and its own grants.
viewuseedit
The shared resource's id (a connector's slug, for that type).
connectiontoolboxtemplateconnector
curl -X POST 'https://api.elaichi.ai/toolbox/<id>/share' \
-H 'Authorization: Bearer $ELAICHI_API_TOKEN' \
-H 'Content-Type: application/json' \
-d '{"grantee_type":"user","level":"view"}'const body = {
"grantee_type": "user",
"level": "view"
};
const response = await fetch('https://api.elaichi.ai/toolbox/<id>/share', {
method: 'POST',
headers: {
'Authorization': 'Bearer ' + process.env.ELAICHI_API_TOKEN,
'Content-Type': 'application/json',
},
body: JSON.stringify(body),
});
const data = await response.json();
console.log(data);import os
import requests
url = "https://api.elaichi.ai/toolbox/<id>/share"
headers = {
"Authorization": f"Bearer {os.environ['ELAICHI_API_TOKEN']}",
"Content-Type": "application/json",
}
payload = {
"grantee_type": "user",
"level": "view"
}
response = requests.post(url, headers=headers, json=payload)
print(response.json())