Get a template
/template/{id}
Includes the full entry list (never a `connection_id`), and `access_summary` when the caller may see the ACL. Resolves docs/access-model.md §4 ONLY — owner or an applicable grant — and a template neither reaches is `404`, never `403`, for everyone including a holder of `template:manage`. Templates carry no oversight fallback: unlike toolboxes and connections, there is no `?visibility=org` opt-in here.
Path Parameters
Template id (tpl_…).
Response Body
The caller's access: owner, or a granted view/use/edit level. Templates carry no organization-wide oversight path — an org owner/admin sees a template only when they own it or it has been shared with them, same as any other member.
Present on a GET /template 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. Same gate and same reasoning as the toolbox row's.
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 template — 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 template 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.
ownerdirectteamorg
Present exactly when access_via is team, absent otherwise. The team the caller reaches this template 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 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.
Whether the caller may edit this template's own settings — owner, or edit access. No template:manage fallback. Mirrors PATCH /template/{id}.
can_share, verbatim — a THIRD formula, distinct from can_manage: an edit grantee whose role omits template:share can edit the template but was never meant to grant or revoke someone else's access to it. Mirrors DELETE /template/{id}/share/{aclId}.
Whether the caller may share this template — owner, edit access, or template:share. No template:manage fallback. Mirrors POST /template/{id}/share's own check.
Whether the caller may transfer or delete this template — ownership, full stop. No template:manage fallback. Mirrors POST /template/{id}/transfer and DELETE /template/{id}.
Whether the caller may stamp a toolbox from this template — 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.
Which integrations this template's tools come from, bounded — the console renders it as a stack of connector logos, exactly as on a toolbox row. Present on every GET /template row AND on every command response (POST /template, GET /template/{id}, PATCH /template/{id}, POST /template/{id}/transfer), so a client that merges a command response into its list does not lose the stack. 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 — 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
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 template.
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.
assistant_disabledllm_not_configurednull
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
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.
Response-only, per READER: may the caller open connector_slug (GET /connector/{slug} and its /tools)? False for an organization-owned custom connector nobody shared with the caller (docs/access-model.md §4). Always true for a platform connector and for a synthetic entry.
Connector slug for proxy entries; null for synthetic.
Entry id (tple_…) — stable across updates only if you send it back unchanged.
Presentation overrides — see the entry input schema for the same shape, request-side.
4 properties
Set for synthetic entries (syn_…).
Response-only. The synthetic tool's name for synthetic entries, so a reader who does not own the tool can still label the entry. Null for proxy entries and when the tool has been deleted.
Connector tool name for proxy entries.
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.
proxysynthetic
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.
Template id (tpl_…).
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 — 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.
User id (usr_…) — same value as owner_user_id.
Creator (usr_…).
Every grant, unabridged. Present only on GET /template/{id} when the caller may see the ACL (owner or edit access); list rows carry the bounded access_summary instead.
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).
connectiontoolboxtemplateconnectorfile
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.
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.
At most 5 names.
At most 5 names.
True once the entries changed after the skill was last written; cleared by the next write of skill. Never true without a skill.
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.
When skill was last written. Null alongside a null skill.
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.
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.
Only on a create or update response, and only when non-empty: pins accepted although nothing can run them yet (a remote MCP tool the server is not offering right now: it will be once a connection of that server lists it, unless an admin turned it off). Informational, never an error.
curl -X GET 'https://api.elaichi.ai/template/<id>' \
-H 'Authorization: Bearer $ELAICHI_API_TOKEN' \
-H 'Content-Type: application/json'const response = await fetch('https://api.elaichi.ai/template/<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/template/<id>"
headers = {
"Authorization": f"Bearer {os.environ['ELAICHI_API_TOKEN']}",
"Content-Type": "application/json",
}
response = requests.get(url, headers=headers)
print(response.json())