Format batch commit: files is a change set, so delete is separate
A batch PUT to a Format's contents commits many files at once. Unnamed files stay, deletes need DELETE, and one stale sha lands nothing.

To change several files of a Sume Format in one commit, send PUT /v1/formats/{handle}/{slug}/contents with a files list and no path. The list is a change set, not the package: every file you do not name is kept exactly as it was, and a batch cannot delete anything. Deleting a file is a separate DELETE call. If any entry is stale or breaks a package rule, nothing is committed.
That shape is deliberate, and it changes how you write the call. A coding agent that edits a Format the way it edits a repository should treat the batch as a patch of named files, never as a snapshot to reconcile.
What does one batch PUT write?
The batch takes a commit message and a files array. Each entry follows the single-file rules, so you can mix edits and new files in one request. The result is one commit, one version bump and one new package_sha, instead of one of each per file.
The table summarizes the rules as documented. Read 2026-10-03 from the Contents API page.
| Part of the request | Rule |
|---|---|
content | The whole file, base64-encoded. It replaces the file; it is not a patch. |
sha | Required when the path already exists. Omit it to create a new file. |
| Paths you do not name | Kept exactly as they are. |
| Deleting a file | Not expressible in a batch. Use DELETE …/contents/{path}. |
A stale sha or a rule violation | Nothing is committed; the batch lands whole or not at all. |
| Result | One commit, one version bump, one new package_sha. |
| Paths per batch | At most 1000. |
Why can't a batch delete a file?
Because a change set that could delete would turn an omission into a deletion. The docs put it plainly: removing a file is always something you asked for and never something you forgot to say. Editing two of twelve files cannot put the other ten at risk.
The practical consequence is a two-step flow when you restructure a package. Commit the new files and edits in one batch, then delete the files you retired with one DELETE each. Each DELETE takes a message and the file's sha, and the reply carries content: null.
What makes the whole batch fail?
Two things, and both are all-or-nothing. A stale per-file sha means someone else committed first: re-read the file and retry. A resulting package that breaks a rule is rejected before anything is committed. The package rules are the same ones the dashboard editor enforces: SKILL.md at the root, file names matching ^[A-Za-z0-9][A-Za-z0-9._-]*$, and only .md, .json, .yaml, .yml and .txt files.
Per-file sha does not notice a package that moved under you. Two agents editing different files each hold a current sha, so both writes land. To close that gap, add an If-Match header carrying the package sha; a mismatch answers 409 format_package_sha_mismatch with the current sha in error.details.package_sha.
What does a safe batch call look like?
Read the package with ?recursive=1 to get every file and its sha, edit what you need, then send only the changed entries.
The commit author is always the key's owner; an author or committer in the body is ignored. Editing never touches a run already in flight, because each run reads the package it started with.
curl -sS -X PUT "https://api.sume.com/v1/formats/acme/live-commerce/contents" \
-H "Authorization: Bearer $SUME_API_KEY" \
-H "If-Match: $PACKAGE_SHA" \
-H "Content-Type: application/json" \
-d '{
"message": "edit plan, add new note",
"files": [
{ "path": "references/plan.md", "content": "IyBQbGFuCg==", "sha": "3f7b1c0d9e2a4b6c8d0e1f2a3b4c5d6e7f809a1b" },
{ "path": "references/new-note.md", "content": "IyBOZXcK" }
]
}'What this API does not do yet
There is no public git endpoint, no clone URL, and no revert, blame or history browsing on this surface. Commits exist because the API makes them. Plan your own record of which package_sha you shipped, and keep the package_sha returned by each write for the next If-Match.
Sources
Related posts
More in Formats
- Format output_schema keywords: pattern and minLength yes, not no
Which JSON Schema keywords a Sume Format output_schema accepts (pattern, minLength, multipleOf, enum, const) and which fail as unsupported_keyword.
- Format output_schema nullable: true is rejected, use a type union
OpenAPI nullable: true fails a Sume output_schema as unsupported_keyword. Declare optional fields as type [string, null] and keep them in required.
- Format output_schema: $ref "#" fails, a $defs self-reference works
Sume rejects root recursion via $ref "#" in output_schema as unsupported_ref but accepts a $defs entry that references itself. How to write a tree.
- Format output_schema root must be an object: wrap an array root
A Sume Format run rejects an output_schema whose root is an array or type union with 400 and rule root_must_be_object. Wrap it in a named key.
Written by Sume