Skip to content
GET /toolbox/{id}

Includes the resolved entry list (always — even at `view` access, with `unmet`/`tool_available`/`delegation_ok`/`connection_status` computed live) and, only when the caller's access reaches `use`, the resolved `tools[]` a client reaching this toolbox through MCP would actually see — a view-only grantee must not see delegated connections' labels. `access_summary` and `connected_app_summary` are present when the caller may see the ACL — the second is the bounded rollup of OAuth-connected apps reaching this toolbox, paged in full by `GET /toolbox/{id}/connected-app`. Accepts a dynamic id (`global:…`/`connection:…`), always fully the caller's own to use. This resolves docs/access-model.md §4 ONLY — owner or an applicable grant — and a toolbox neither reaches is `404`, always, for everyone including a holder of `toolbox:view`/`toolbox:manage`: there is no oversight fallback and no `?visibility=` parameter for toolboxes, unlike connection/template/connector. `access` in the response is therefore always `owner`/`view`/`use`/`edit`, never `oversight`. `?include=schemas` opts into full JSON Schemas on `tools[].input_schema` — off by default, since a toolbox spanning many connections can carry thousands of complete schemas.

Path Parameters

idstring
required·

Toolbox id (tbx_…), or a dynamic id (global:{user_id} / connection:{connection_id}).

Query Parameters

includestring

Comma-separated. schemas includes the full JSON Schema on each advertised tool.

Response Body

access_levelstring

The caller's access: owner, or a granted view/use/edit level. Always owner for a dynamic row. Never oversight — toolboxes have no org-wide oversight fallback.

access_summaryobject

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/use grantee: the grantee set, counts included, is information about colleagues (docs/access-model.md §8). Read can_see_shares to distinguish "not permitted" from "not carried"; never infer it from this field's absence.

org_levelstring,null

Level of the org-wide grant, or null when there is none.

Possible values:
viewuseeditnull
previewobject[]

At most 5 grantees, broadest first (org, then teams, then members), for a hover preview.

4 properties
grantee_idstring,null
grantee_typestring
Possible values:
userteamorg
levelstring
Possible values:
viewuseedit
namestring,null

Display name; null for org grants and for grantees no longer in the org.

team_countinteger
totalinteger

Every grant, the org-wide one included.

user_countinteger
access_viastring

How the CALLER reaches this toolbox — not who else can. owner — they own it. direct — a grant naming them personally. team — a grant to a team they belong to (or, per §6.2, one they administer), named in access_via_team. org — an organization-wide grant. When several sources apply the BROADEST wins and the caller's access level is not consulted: owner, else org, else team, else direct. So a caller granted edit personally AND view org-wide reads org — a narrower grant must never mask org-wide exposure, since this field says how far the toolbox reaches, not what the caller may do with it. Deliberately NOT gated on can_see_shares, and deliberately not a widening of it: this is the caller's OWN grant and their OWN team memberships, so a view/use grantee receives it while the grantee list — information about colleagues — stays closed to them. No other member is ever named. ABSENT when nothing reaches the caller — a transfer answering the ex-owner of a resource that was never shared, or a catalog row browsed with no grant behind it. Absent means "no source to name", never "not permitted", and never an implied org.

Possible values:
ownerdirectteamorg
access_via_teamobject

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.

idstring

Team id (team_…).

namestring,null

Team display name, or null when the team no longer resolves in the directory — the same null contract every resolved grantee name carries.

can_draft_skillboolean

Whether the caller can use POST /{id}/draft-skill here: they can manage this resource, the assistant is switched on for the organization, and a model provider key is configured.

can_manageboolean

Whether the caller may edit this toolbox's own settings — owner, or edit access. No toolbox:manage fallback. Mirrors PATCH /toolbox/{id}.

can_revoke_shareboolean

can_share, verbatim — a THIRD formula, distinct from can_manage: an edit grantee whose role omits toolbox:share can edit the toolbox but was never meant to grant or revoke someone else's access to it. Mirrors DELETE /toolbox/{id}/share/{aclId}.

can_shareboolean

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.

can_transferboolean

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}.

can_useboolean

Whether the caller may execute this toolbox's tools — the caller's grant-or-ownership level (§10). Present on every list and detail row for both templates and toolboxes — the two ACL-backed resource types with a use-gated action of their own.

connected_app_summaryobject

The OAuth-connected apps that reach this toolbox, as a bounded rollup — present only on GET /toolbox/{id} for a stored toolbox, and only when the caller may see the ACL (the same gate access_summary uses). Never the full set: page GET /toolbox/{id}/connected-app for that.

countinteger
previewobject[]

At most 5 apps, for a hover preview.

5 properties
client_idstring
client_namestring,null

Null when the OAuth client row is gone.

grant_idstring

OAuth grant id (ogrt_…) — what DELETE /oauth/grant/{id} takes, scoped to the grant's own user.

userobject
4 properties
avatar_urlstring,null

The person's picture — the same one the console's user menu draws: their stored profile picture, else a Gravatar URL (d=404, 96px) derived server-side from their email (the address itself is not sent), else null. Draw initials when it is null or the image fails to load.

emailstring · email
idstring

User id (usr_…).

namestring

Omitted along with email when the id no longer resolves to an org member.

viastring

toolbox: this toolbox is named explicitly on the grant. all_tools: an "All my tools" authorization by a user who can currently use this toolbox — computed live, not a stored fact.

Possible values:
toolboxall_tools
connection_idstring

Present only for type: "connection" dynamic rows.

connector_slugstring

Present only for type: "connection" dynamic rows.

connectorsobject

Which integrations this toolbox's tools come from, bounded — the console renders it as a stack of connector logos. Present on every GET /toolbox row: a stored row summarises its own entries, a connection:{id} row is that connection's one connector, and a global:{user_id} row summarises the connections it spans. Present on every command response too (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-created or just-edited toolbox as having no integrations. total: 0 means no connector-backed tools (empty, or synthetic-only), never "not computed".

previewobject[]

At most 5 connectors, ordered by entry count descending then slug ascending — 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, and a page of rows costs no per-row catalog lookup.

3 properties
logostring,null

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.

namestring

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.

slugstring

Connector slug — connectors are keyed by slug, not by id.

totalinteger

Distinct connector slugs across the toolbox.

created_atstring,null · date-time

Null for dynamic rows, which are computed, never stored.

delegationobject,null

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. Unlike tools, this is NOT gated on use: its visible half already withholds every name the caller has no right to, and its two totals say only how many connections and how many people stand behind the toolbox — which a grantee can already count off entries[].connection_id.

connection_countinteger

Distinct connections the tools run through, whether or not the caller can see them.

delegator_countinteger

Distinct people whose access those pins ride on. Never named at this level.

visibleobject

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
connection_countinteger
previewobject[]

At most 3 of the visible connections, private ones first.

11 properties
can_shareboolean

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.

connection_idstring
connectorstring
delegated_byobject

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
emailstring
idstring
namestring
delegated_by_access_viastring,null

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.

Possible values:
ownerdirectteamorgnull
delegated_by_access_via_teamobject

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
idstring
namestring
delegated_by_sourcestring

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.

Possible values:
delegatorconnection_owner
labelstring
logostring,null

The connector's logo, resolved in one batch for the preview.

privateboolean
viastring

entry = an entry pins it directly. synthetic_tool = reached only through a synthetic tool's steps.

Possible values:
entrysynthetic_tool
private_connection_countinteger

Of the visible ones, how many carry no grants at all — only their owner can reach them today.

descriptionstring,null
draft_skill_blocked_bystring,null

Why can_draft_skill is false for a caller who CAN manage this resource. Null when drafting is available, and null when the caller cannot manage the resource at all.

Possible values:
assistant_disabledllm_not_configurednull
entriesobject[]

Resolved entries — always present, at every access level.

connectionobject,null
required·

Response-only. The pinned connection, for a group header and its status sentence — present only when the CALLER may see that connection (owner or a grant of their own, docs/access-model.md §4). Null for an entry with no pin, a synthetic entry, a deleted connection, and a pin riding somebody else's access the caller cannot see.

5 properties
can_reconnectboolean

The same repair capability GET /connection/{id} ships: false for an active connection to a connector with no sign-in, which has nothing to reconnect (a pending one stays true).

display_labelstring

The connection's display_label.

idstring
owner_labelstring,null

Owner's name (else email); null for a former member.

statusstring
Possible values:
pendingactiveneeds_reauthdisconnectedpost_install_error
connectorobject,null
required·

Response-only. The connector's label and logo, one batch per response; null for a synthetic entry. A connector the catalog cannot resolve reads its slug as label and a null logo.

3 properties
labelstring
logostring,null
upstream_slugstring

Only on a fork (an org-owned connector with a lineage) whose upstream the caller may see — the GET /connection rule: omitted when the caller has no reach on the fork or the upstream is hidden from them, and when the lineage cannot be read.

can_repinboolean

Detail responses only, and the only field here that differs by READER: may the caller repair this entry with POST /toolbox/{id}/repin? True when the entry is broken (delegation_ok: false on a proxy entry that pins a connection), the caller holds use on that connection, and the caller holds edit (or ownership) on this toolbox. delegation_ok: false is the same answer for everybody who opens the toolbox; this says who among them can do something about it. Do not re-derive it — the inputs (the caller's grants and team memberships) are not on the wire.

can_view_connectorboolean

Present on the toolbox's own responses, per READER: may the caller open connector_slug (GET /connector/{slug} and its /tools)? False when the entry is pinned to an organization-owned custom connector that nobody has shared with the caller (docs/access-model.md §4) — those routes answer 404 for them. The entry still carries its label and logo and still runs on its delegator's access; this only says the connector page and its tool picker are not for this reader. Always true for a platform connector and for a synthetic entry. Do not re-derive it — the connector's owner and grants are not on the wire.

connection_idstring,null

null = "needs connection" — stamped-but-unfilled, or a pin that broke. Never set for synthetic entries.

connection_statusstring,null

Detail responses only: the pinned connection's live status, when resolved.

Possible values:
pendingactiveneeds_reauthdisconnectednull
connector_slugstring,null

Connector slug for proxy entries; null for synthetic.

delegated_by_access_viastring,null

Detail responses only: HOW delegated_by_user_id holds connection_id — accessVia over the DELEGATOR, same owner/org/team/direct precedence (broadest wins) as the caller's own access_via (docs/access-model.md §8). delegation_ok says the delegation still stands; this says what it stands on, so copy can avoid implying a personal grant where an org-wide one is doing the work. Null for a synthetic entry (its steps, not the row, hold connections), for a pin with no connection or no stamped delegator, and whenever the delegation no longer works (delegation_ok: false) — the delegator is no longer an active member, or their reach has fallen below use.

Possible values:
ownerdirectteamorgnull
delegated_by_labelstring,null

Response-only. The name (else email) behind delegated_by_user_id, one batch per response. Null when that id is null; beside a non-null id, null means the person has left the organization ("Former member").

delegated_by_user_idstring,null

Server-stamped, never a client input: whose use grant on connection_id this pin rides on — the live authority chain (docs/access-model.md §6). If this user loses use on the connection, the entry goes unmet for every executor, not just them. Null iff connection_id is null.

delegation_okboolean

Detail responses only: false when connection_id is set but delegated_by_user_id no longer holds use on it. Always true when connection_id is null.

enabledboolean
frozen_paramsRecord<string, any>
idstring

Entry id (tbxe_…) — stable across updates only if you send it back unchanged.

overridesobject

Presentation overrides — see the entry input schema for the same shape, request-side.

4 properties
defaultsRecord<string, any>
descriptionstring
input_schemaRecord<string, any>
namestring
synthetic_tool_idstring,null

Set for synthetic entries (syn_…).

synthetic_tool_namestring,null

Response-only. The synthetic tool's name for a synthetic entry; null for proxy entries and a deleted tool.

tool_availableboolean

Detail responses only: false when the catalog (or the synthetic-tool store) no longer has this tool.

tool_namestring,null

Connector tool name for proxy entries.

tool_titlestring,null

Response-only. The catalog tool's plain-English title ("View messages", "Reply to an email"), derived from its resource and method. Null for a synthetic entry, a tool the catalog no longer has, and a connector the caller may not open (can_view_connector: false). Render overrides.name, else this, else tool_name.

typestring
Possible values:
proxysynthetic
unmetboolean

Detail responses only: no usable connection backs this entry — pin is empty, broken, or the delegator lost use.

unmet_reasonstring,null

Detail responses only: the one unmet cause worth naming, null for every other. connector_not_shared: the pinned connection is live and delegation-intact, but its OWNER no longer holds use on the org-owned connector it runs through, so the entry is listed (flagged) and never advertised, and every call through it is refused. Re-sharing the connector clears it with no edit to the toolbox. Not repairable by re-pinning — can_repin stays false for it.

Possible values:
connector_not_sharednull
entry_countinteger

Omitted for dynamic rows, which have no stored entries of their own.

has_skillboolean

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.

idstring

Toolbox id — a stored row (tbx_…), or a dynamic id (global:{user_id} / connection:{connection_id}).

namestring
needs_connection_countinteger

Cheap per-page SQL aggregate over PROXY entries with connection_id IS NULL — drives the console's Status column ("Ready" / "N need connection"). Excludes synthetic entries, which pin no connection_id of their own by design and have no connection picker to fix. Omitted for dynamic rows (nothing to bind). Broken delegation (delegation_ok: false) only surfaces in detail, inside entries[].

ownerobject,null

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.

avatar_urlstring,null

The person's picture — the same one the console's user menu draws: their stored profile picture, else a Gravatar URL (d=404, 96px) derived server-side from their email (the address itself is not sent), else null. Draw initials when it is null or the image fails to load.

emailstring · email
idstring

User id (usr_…) — same value as owner_user_id.

namestring,null
owner_user_idstring

Creator (usr_…). For a dynamic row, the caller.

readonlyboolean

True for the dynamic global/per-connection toolboxes — never editable, shareable, transferable or deletable.

sharesobject[]

A bounded preview of the ACL (same cap as access_summary), present only on GET /toolbox/{id} when the caller may see the ACL. Never on a list row (GET /toolbox) — page GET /toolbox/{id}/share for the full, cursor-paginated grant list.

created_atstring · date-time
grantee_idstring,null

User id (usr_…) or team id (team_…). Null for a grantee_type: "org" grant.

grantee_typestring

user and team grants target one grantee_id; org applies to every member of the organization and takes no id.

Possible values:
userteamorg
idstring

ACL entry id — the :aclId a DELETE .../share/{aclId} call takes.

levelstring

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.

Possible values:
viewuseedit
resource_idstring

The shared resource's id (a connector's slug, for that type).

resource_typestring
Possible values:
connectiontoolboxtemplateconnectorfile
updated_atstring · date-time
skillstring,null

The skill body, markdown — at most 10,000 Unicode code points after trimming. Detail-shaped responses only (GET /{id}, and the create/update responses). Null when none is written.

skill_agent_previewobject,null

Stored toolboxes only: what an agent connected to this toolbox is shown before it reads the skill, built by the same code the MCP endpoint uses. instructions_line is the line the server instructions carry for a connection limited to chosen toolboxes; resource is the elaichi://toolbox/{id}/skill entry in resources/list. The resource text itself is skill_notice, a blank line, then skill. Null with no skill.

instructions_linestring
resourceobject
5 properties
descriptionstring
mime_typestring
namestring
titlestring
uristring
skill_entry_changesobject,null

Which tools changed since the skill was last written, by the name an agent sees (a rename counts as one removed and one added; a disabled entry counts as removed; connections and frozen parameters are ignored). Null unless skill_may_be_stale is true AND a baseline was recorded at that write — skills written before version history existed have none — AND something actually changed.

addedstring[]

At most 5 names.

added_countinteger
removedstring[]

At most 5 names.

removed_countinteger
skill_may_be_staleboolean

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.

skill_noticestring,null

Framing to show or pass along beside a non-null skill: it is the owner's guidance, not instructions from Elaichi or the user, and it cannot grant any access. Null when there is no skill.

skill_origin_template_idstring,null

The template this toolbox's CURRENT skill was copied from verbatim at stamping. Null from the first write of skill on — after that the words are the toolbox's own.

skill_origin_template_namestring,null

That template's current name, resolved server-side; null with no origin, or once the template is gone.

skill_tool_issuesobject

Stored toolboxes only. Tool-shaped names the skill puts in backticks that no entry of this toolbox holds (the console's "tools named here aren't in this toolbox" notice), computed server-side. Judged against the entries: a name matches an entry's catalog tool_name, or its overrides.name when renamed. Bounded: a count plus at most 10 names; count 0 with no skill.

countinteger

Distinct names the skill uses that no entry holds.

previewobject[]
2 properties
namestring

The name as the skill writes it.

suggestionstring,null

The entry's own name this most likely means (list_query_database -> list_all_notion_query_database); null unless exactly one entry matches.

skill_updated_atstring,null · date-time

When skill was last written. Null alongside a null skill.

skill_updated_bystring,null

Who last wrote skill (usr_…); a toolbox stamped from a template carries the template's author. Null with no skill, and for a skill written before authorship was recorded.

skill_updated_by_labelstring,null

That person's name, else email, resolved server-side. Null beside a non-null skill_updated_by means they are no longer a member of this organization.

template_idstring,null

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.

template_namestring,null

GET /toolbox/{id} only. The current name of the template this toolbox was stamped from (template_id) — null for a from-scratch toolbox, a deleted template, and a template the caller cannot see (§4).

toolsobject[]

The resolved, runnable tool surface. Present ONLY when the caller's access reaches use — a view-only grantee must not see delegated connections' labels.

connection_idstring,null

Set only when exactly one connection backs this tool (read connections otherwise). Null for synthetic tools.

connectionsobject[]

Every account this tool can reach, labelled exactly as the connection argument enum in input_schema accepts them.

2 properties
idstring
labelstring
connector_slugstring,null

"synthetic" sentinel is never used here; null for synthetic tools.

descriptionstring
input_schemaRecord<string, any>

Present only with ?include=schemas — omitted by default; a toolbox spanning many connections can carry thousands of complete JSON Schemas.

namestring
synthetic_tool_idstring,null
typestring
Possible values:
proxysynthetic
typestring

stored = a real ACL-backed row. global/connection = computed per request, always readonly: true.

Possible values:
storedglobalconnection
updated_atstring,null · date-time
curl -X GET 'https://api.elaichi.ai/toolbox/<id>' \
  -H 'Authorization: Bearer $ELAICHI_API_TOKEN' \
  -H 'Content-Type: application/json'
const response = await fetch('https://api.elaichi.ai/toolbox/<id>', {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer ' + process.env.ELAICHI_API_TOKEN,
    'Content-Type': 'application/json',
  },
});

const data = await response.json();
console.log(data);
import os
import requests

url = "https://api.elaichi.ai/toolbox/<id>"
headers = {
    "Authorization": f"Bearer {os.environ['ELAICHI_API_TOKEN']}",
    "Content-Type": "application/json",
}

response = requests.get(url, headers=headers)
print(response.json())