Ingram Cloud

Documentation

Skills

Skills

A skill is a folder anchored by SKILL.md, per the Agent Skills specification: frontmatter (name, description, …) plus a body of instructions, and optional references/, scripts/, and assets/ subdirectories. Where hosted and MCP tools give a smith functions to call, a skill gives it material to read — a procedure, a house style, a reference doc — that it pulls in only when the task calls for it, instead of every turn.

Uploading a skill

POST /v1/skills takes the bundle either as one zip on a file part, or as repeated files[] parts whose filenames carry each path relative to — and including — the skill's root directory. Either way, creates the skill and its first version:

# Authorization: tenant-admin token (server-side only)
curl -X POST https://api.cloud.ingram.tech/v1/skills \
  -H "Authorization: Bearer $IC_TOKEN" \
  -H "IC-Api-Version: 2026-05-01" \
  -F "file=@invoice-review.zip"
# → 200 { "id": "skl_…", "object": "skill", "name": "invoice-review",
#         "description": "Use when reviewing a supplier invoice.",
#         "default_version": 1, "created_at": "…", "updated_at": "…" }

The equivalent as repeated files[] parts:

# Authorization: tenant-admin token (server-side only)
curl -X POST https://api.cloud.ingram.tech/v1/skills \
  -H "Authorization: Bearer $IC_TOKEN" \
  -H "IC-Api-Version: 2026-05-01" \
  -F "files[]=@invoice-review/SKILL.md;filename=invoice-review/SKILL.md" \
  -F "files[]=@invoice-review/references/vat.md;filename=invoice-review/references/vat.md"

A zip may not unpack to more than 32 MB, and a bundle carries at most 500 files. SKILL.md and everything under references/ come to at most 1 MB together — they are what the model searches, and past that the content is retrieval-shaped, which is a vector store. scripts/ and assets/ are not counted: they are read on demand and never indexed.

name is the spec's skill name, parsed from SKILL.md's frontmatter — it must match the bundle's root directory, and it is the tenant-unique handle: a second POST /v1/skills with the same name is refused with 409 skill_exists. GET /v1/skills lists a tenant's skills; GET /v1/skills/{id} fetches one; DELETE /v1/skills/{id} deletes it, refused (409 skill_in_use) while any published agent version still references it.

Versions

A skill's versions are immutable and integer-numbered. Publish a new one the same way you created the skill, against /versions:

# Authorization: tenant-admin token (server-side only)
curl -X POST https://api.cloud.ingram.tech/v1/skills/skl_.../versions \
  -H "Authorization: Bearer $IC_TOKEN" \
  -H "IC-Api-Version: 2026-05-01" \
  -F "files[]=@invoice-review/SKILL.md;filename=invoice-review/SKILL.md"
# → 200 { "skill_id": "skl_…", "version": 2, … }

Publishing a version never moves default_version — an agent that names no explicit version keeps running the one it already resolved to. Move it explicitly:

# Authorization: tenant-admin token (server-side only)
curl -X POST https://api.cloud.ingram.tech/v1/skills/skl_... \
  -H "Authorization: Bearer $IC_TOKEN" \
  -H "IC-Api-Version: 2026-05-01" \
  -H "Content-Type: application/json" \
  -d '{ "default_version": 2 }'

GET /v1/skills/{id}/versions lists a skill's versions, newest first; GET /v1/skills/{id}/versions/{v} returns one, including the file list with each file's size and content hash (sha256), and referenced_by — the published agent versions that froze a reference to it (the same list a 409 skill_in_use names). Bytes never appear in this response, so this is how a tenant sees exactly what they installed without the bytes themselves. DELETE …/versions/{v} deletes one version, refused (409 skill_in_use) when it is the skill's default_version or a published agent version still references it.

Reading a version back

# Authorization: tenant-admin token (server-side only)
curl https://api.cloud.ingram.tech/v1/skills/skl_.../versions/2/content \
  -H "Authorization: Bearer $IC_TOKEN" \
  -H "IC-Api-Version: 2026-05-01" \
  -o invoice-review-v2.zip

Without ?path= the whole version comes back as a zip — the same shape the upload accepts, so a version round-trips. With ?path=references/vat.md, that one file's bytes.

Attaching a skill to an agent

An agent's draft carries a skills field alongside enabled_hosted_tools and mcp_servers — the list of skills its smiths carry:

# Authorization: tenant-admin token (server-side only)
curl -X PATCH https://api.cloud.ingram.tech/v1/agents/agt_... \
  -H "Authorization: Bearer $IC_TOKEN" \
  -H "IC-Api-Version: 2026-05-01" \
  -H "Content-Type: application/json" \
  -d '{ "skills": [ { "skill_id": "skl_..." } ] }'

A skill_id with no version resolves to the skill's default_version at publish time: POST …/versions writes that number into the snapshot, so moving default_version afterwards never changes what an already-published agent version runs. Publishing refuses a skill_id or a version that doesn't resolve (422) — a frozen snapshot can't be corrected later.

A smith may override its agent's skills the same way it overrides mcp_servers — PATCH the smith; an explicit null clears the override and re-inherits the agent's. A smith's own override is live config, not a snapshot: omit version there and it follows default_version.

What the model gets

A skill's name and description are on every turn — cheap enough that the model can decide whether the skill is relevant. The body of SKILL.md, and anything under references/, is read only when the model decides the skill applies, so an agent with many skills does not pay full context for all of them every turn. skill_search searches the body and every reference file by relevance, so the model can find one paragraph without reading the whole bundle.

A skill's scripts/ run through the run_command hosted tool — an agent that attaches a skill carrying scripts needs run_command enabled, or the model can read the script but never run it. The sandbox box unpacks every skill the run carries to /skills/<name>/ before the first command, read-only. A script writes its output to /workspace (this run) or /cache (this smith); a write under /skills is refused.

A skill that cannot be served does not fail the run — it becomes a skill_unavailable warning on the run and on its run.completed event, so an answer produced without a skill is never shaped like one produced with it. The same applies when a run's skills together exceed the search-index ceiling (4 MB of SKILL.md plus references/): the run keeps them in the order the skills array lists and drops the rest, warning for each — so ordering that array is how you say which skills matter most.

In the console

Skills in the console lists your skills, what each one carries, and which agent versions froze it. Upload a bundle as a zip, publish a version, move default_version, and download any version whole or file by file. An agent's editor attaches skills in order — the same order that decides which survive a run's search-index ceiling.