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.

4 min readSume
All posts

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.

Batch PUT rules, from docs.sume.com/formats/contents (read 2026-10-03)
Part of the requestRule
contentThe whole file, base64-encoded. It replaces the file; it is not a patch.
shaRequired when the path already exists. Omit it to create a new file.
Paths you do not nameKept exactly as they are.
Deleting a fileNot expressible in a batch. Use DELETE …/contents/{path}.
A stale sha or a rule violationNothing is committed; the batch lands whole or not at all.
ResultOne commit, one version bump, one new package_sha.
Paths per batchAt 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

All Formats posts

Written by Sume