Transfer toolbox ownership
/toolbox/{id}/transfer
Changes `owner_user_id` — and nothing else. Every existing grant is left exactly as it was, so nobody who could already reach it loses that reach; the new owner gains only what ownership itself confers (implicit `edit`, plus delete and transfer). Each entry's own delegator (whoever held `use` when they pinned it) is untouched by an ownership transfer of the TOOLBOX — that is a separate fact from who owns the pinned connection. Rejects a dynamic id with 400. Allowed for the current owner only — no `toolbox:manage` fallback.
Path Parameters
Stored toolbox id (tbx_…).
Request Body
New owning member (usr_…).
Response Body
The caller's access: owner, or a granted view/use/edit level. Always owner for a dynamic row.
Present on a GET /toolbox stored row exactly when that row's can_see_shares is true: the caller owns it, holds edit, or administers a team it is granted to. Absent from dynamic rows, and deliberately absent for a view or use grantee, because who else has access is information about colleagues. Read can_see_shares to tell "not permitted" from "not carried"; never infer it from this field's absence.
Level of the org-wide grant, or null when there is none.
viewuseeditnull
At most 5 grantees, broadest first (org, then teams, then members), for a hover preview.
4 properties
userteamorg
viewuseedit
Display name; null for org grants and for grantees no longer in the org.
Every grant, the org-wide one included.
How the caller reaches this toolbox, not who else can. owner: they own it. direct: a grant names them personally. team: a grant to a team they belong to (or administer), named in access_via_team. org: an organization-wide grant. When several sources apply, the broadest wins and the access level plays no part: owner, else org, else team, else direct. A caller with edit personally and view org-wide reads org, because this field says how far the toolbox reaches, not what the caller may do with it. It describes only the caller's own access, so a view or use grantee still gets it while the list of other grantees stays hidden. Absent when nothing reaches the caller (for example, the former owner after a transfer of a resource that was never shared); absent never means org.
ownerdirectteamorg
Present exactly when access_via is team, absent otherwise. The team the caller reaches this toolbox through — one of their own teams, never a disclosure about anybody else.
Team id (team_…).
Team display name, or null when the team no longer resolves in the directory — the same null contract every resolved grantee name carries.
Whether the caller may edit this toolbox's own settings — owner, or edit access. No toolbox:manage fallback. Mirrors PATCH /toolbox/{id}.
Whether the caller may grant or revoke access to this toolbox. It matches can_share and is separate from can_manage: an edit grantee whose role omits toolbox:share can edit the toolbox but cannot change who else has access. Mirrors DELETE /toolbox/{id}/share/{aclId}.
Whether the caller may share this toolbox — owner, edit access, or toolbox:share. No toolbox:manage fallback. Mirrors POST /toolbox/{id}/share's own check.
Whether the caller may transfer or delete this toolbox — ownership, full stop. No toolbox:manage fallback. Mirrors POST /toolbox/{id}/transfer and DELETE /toolbox/{id}.
Whether the caller may execute this toolbox's tools — based on the caller's ownership or grant level. Present on every list and detail row for both templates and toolboxes.
The OAuth-connected apps that reach this toolbox, as a short summary. Present only on GET /toolbox/{id} for a stored toolbox, and only when the caller may see who it is shared with (the same condition as access_summary). Never the full set: page GET /toolbox/{id}/connected-app for that.
At most 5 apps, for a hover preview.
5 properties
Null when the OAuth client row is gone.
OAuth grant id (ogrt_…) — what DELETE /oauth/grant/{id} takes, scoped to the grant's own user.
4 properties
The person's picture: their stored profile picture, else a Gravatar URL (d=404, 96px) derived from their email (the address itself is not sent), else null. Draw initials when it is null or the image fails to load.
User id (usr_…).
Omitted along with email when the id no longer resolves to an org member.
toolbox: this toolbox is named explicitly on the grant. all_tools: an "Everything I can use" authorization by a user who can currently use this toolbox — computed live, not a stored fact.
toolboxall_tools
Present only for type: "connection" dynamic rows.
Present only for type: "connection" dynamic rows.
Which integrations this toolbox's tools come from, summarized as at most a handful of connectors. Present on every GET /toolbox row: a stored row summarizes its own entries, a connection:{id} row is that connection's one connector, and a global:{user_id} row summarizes the connections it spans. Also present on every command response (POST /toolbox, GET /toolbox/{id}, PATCH /toolbox/{id}, POST /toolbox/{id}/transfer), so a client that merges one into its list does not show a just-edited toolbox as having no integrations. total: 0 means no connector-backed tools (empty, or synthetic-only), never "not computed".
At most 5 connectors, ordered by entry count descending then slug ascending, so the dominant integration leads and the order is stable across requests. Each entry arrives resolved: there is no follow-up GET /connector/{slug} to make.
3 properties
The connector's square icon when it has one, else its wordmark logo, else null (the connector carries neither picture, or could not be resolved). Draw it in a square tile; render initials from name when it is null.
Catalog label. Falls back to the slug when the connector no longer resolves (deleted from the catalog), so a row always has something to draw.
Connector slug — connectors are keyed by slug, not by id.
Distinct connector slugs across the toolbox.
Null for dynamic rows, which are computed, never stored.
Omitted for dynamic rows, which have no stored entries of their own.
Whether a skill is written. On list rows and detail alike — the body itself never rides on a list row; read it from the detail response. Omitted on a dynamic row, which has no skill of its own.
Toolbox id — a stored row (tbx_…), or a dynamic id (global:{user_id} / connection:{connection_id}).
Number of proxy entries with no connection pinned (connection_id is null), which is what a "N need connection" status is built from. Excludes synthetic entries, which pin no connection_id of their own by design. Omitted for dynamic rows (nothing to bind). Broken delegation (delegation_ok: false) only surfaces in the detail, inside entries[].
Owner summary — resolved for a row the caller does not themselves own (a toolbox shared with them), so the UI always knows whose row it is looking at. Absent for a toolbox the caller owns.
The person's picture: their stored profile picture, else a Gravatar URL (d=404, 96px) derived from their email (the address itself is not sent), else null. Draw initials when it is null or the image fails to load.
User id (usr_…) — same value as owner_user_id.
Creator (usr_…). For a dynamic row, the caller.
True for the dynamic global/per-connection toolboxes — never editable, shareable, transferable or deletable.
A short preview of who the toolbox is shared with (same cap as access_summary). Present only on GET /toolbox/{id} when the caller may see who it is shared with. Never on a list row (GET /toolbox); page GET /toolbox/{id}/share for the full, cursor-paginated list.
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
Grant id (acl_…), the {aclId} that DELETE .../share/{aclId} 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).
connectiontoolboxtemplateconnectorautomationcollectiondashboardknowledge_basebundlefile
True once the entries changed after the skill was last written; cleared by the next write of skill. Never true without a skill. Omitted on a dynamic row.
Provenance only (stamped at creation, never a live link) — null for a from-scratch toolbox and for every dynamic row. May dangle after the template is deleted.
stored = a real, shareable row. global/connection = computed per request, always readonly: true.
storedglobalconnection
curl -X POST 'https://api.elaichi.ai/toolbox/<id>/transfer' \
-H 'Authorization: Bearer $ELAICHI_API_TOKEN' \
-H 'Content-Type: application/json' \
-d '{"owner_user_id":"your_owner_user_id"}'const body = {
"owner_user_id": "your_owner_user_id"
};
const response = await fetch('https://api.elaichi.ai/toolbox/<id>/transfer', {
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>/transfer"
headers = {
"Authorization": f"Bearer {os.environ['ELAICHI_API_TOKEN']}",
"Content-Type": "application/json",
}
payload = {
"owner_user_id": "your_owner_user_id"
}
response = requests.post(url, headers=headers, json=payload)
print(response.json())