---
title: "Transfer toolbox ownership"
method: POST
path: "/toolbox/{id}/transfer"
tags: ["Toolboxes"]
---

# Transfer toolbox ownership

`POST /toolbox/{id}/transfer`

## Request

- Base URL: `https://api.elaichi.ai`
- URL: `https://api.elaichi.ai/toolbox/{id}/transfer`
- Auth: HTTP bearer

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

- `id` string, required

## Headers

- `X-Organization-Id` string

## Request body

- object
  - `owner_user_id` string, required: New owning member (`usr_…`).

## Response `200`

OK

- object
  - `id` string: Toolbox id — a stored row (`tbx_…`), or a dynamic id (`global:{user_id}` / `connection:{connection_id}`).
  - `name` string
  - `description` string, nullable
  - `owner_user_id` string: Creator (`usr_…`). For a dynamic row, the caller.
  - `type` 'stored' | 'global' | 'connection': `stored` = a real, shareable row. `global`/`connection` = computed per request, always `readonly: true`.
  - `readonly` boolean: True for the dynamic global/per-connection toolboxes — never editable, shareable, transferable or deletable.
  - `template_id` string, nullable: 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.
  - `connection_id` string: Present only for `type: "connection"` dynamic rows.
  - `connector_slug` string: Present only for `type: "connection"` dynamic rows.
  - `access_level` string: The caller's access: `owner`, or a granted `view`/`use`/`edit` level. Always `owner` for a dynamic row.
  - `owner` object, nullable: 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.
    - `id` string: User id (`usr_…`) — same value as `owner_user_id`.
    - `name` string, nullable
    - `email` string, email
    - `avatar_url` string, nullable: 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.
  - `entry_count` integer: Omitted for dynamic rows, which have no stored entries of their own.
  - `needs_connection_count` integer: 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[]`.
  - `connectors` object: 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".
    - `total` integer: Distinct connector slugs across the toolbox.
    - `preview` object[]: 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.
      - `slug` string: Connector slug — connectors are keyed by slug, not by id.
      - `name` string: 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.
      - `logo` string, nullable: 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.
  - `created_at` string, date-time, nullable: Null for dynamic rows, which are computed, never stored.
  - `updated_at` string, date-time, nullable
  - `access_summary` object: 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.
    - `org_level` 'view' | 'use' | 'edit' | 'null', nullable: Level of the org-wide grant, or null when there is none.
    - `team_count` integer
    - `user_count` integer
    - `total` integer: Every grant, the org-wide one included.
    - `preview` object[]: At most 5 grantees, broadest first (org, then teams, then members), for a hover preview.
      - `grantee_type` 'user' | 'team' | 'org'
      - `grantee_id` string, nullable
      - `level` 'view' | 'use' | 'edit'
      - `name` string, nullable: Display name; null for org grants and for grantees no longer in the org.
  - `shares` AclEntry[]: 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.
    - `id` string: Grant id (`acl_…`), the `{aclId}` that `DELETE .../share/{aclId}` takes.
    - `resource_type` 'connection' | 'toolbox' | 'template' | 'connector' | 'file'
    - `resource_id` string: The shared resource's id (a connector's slug, for that type).
    - `grantee_type` 'user' | 'team' | 'org': `user` and `team` grants target one `grantee_id`; `org` applies to every member of the organization and takes no id.
    - `grantee_id` string, nullable: User id (`usr_…`) or team id (`team_…`). Null for a `grantee_type: "org"` grant.
    - `level` 'view' | 'use' | 'edit': 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.
    - `created_at` string, date-time
    - `updated_at` string, date-time
  - `connected_app_summary` object: 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.
    - `count` integer
    - `preview` object[]: At most 5 apps, for a hover preview.
      - `grant_id` string: OAuth grant id (`ogrt_…`) — what `DELETE /oauth/grant/{id}` takes, scoped to the grant's own user.
      - `client_id` string
      - `client_name` string, nullable: Null when the OAuth client row is gone.
      - `user` object
        - `id` string: User id (`usr_…`).
        - `name` string: Omitted along with `email` when the id no longer resolves to an org member.
        - `email` string, email
        - `avatar_url` string, nullable: 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.
      - `via` 'toolbox' | 'all_tools': `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.
  - `has_skill` boolean: 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.
  - `skill_may_be_stale` boolean: 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.
  - `can_use` boolean: 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.
  - `can_share` boolean: 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_manage` boolean: Whether the caller may edit this toolbox's own settings — owner, or `edit` access. No `toolbox:manage` fallback. Mirrors `PATCH /toolbox/{id}`.
  - `can_transfer` boolean: 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_revoke_share` boolean: 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}`.
  - `access_via` 'owner' | 'direct' | 'team' | 'org': 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`.
  - `access_via_team` object: 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.
    - `id` string: Team id (`team_…`).
    - `name` string, nullable: Team display name, or null when the team no longer resolves in the directory — the same null contract every resolved grantee name carries.

## Other responses

- `400`: Bad request
- `401`: Unauthorized
- `403`: Forbidden
- `404`: Not found
- `429`: Rate limited

---

[API](https://skmtc.dev/elaichi/apis/elaichi-api.md) · [All operations](https://skmtc.dev/elaichi/apis/elaichi-api/llms.txt) · [OpenAPI document](https://skmtc.dev/elaichi/apis/elaichi-api/revisions/b22cfb7b6d15?raw)
