Format Contents API: If-Match stops two agents overwriting each other
Per-file sha checks do not catch two agents editing different files. Send If-Match with package_sha and handle 409 format_package_sha_mismatch.

To keep two agents from overwriting each other's work on a Sume Format, send If-Match: <package_sha> on every write. A per-file sha only protects the file you touch. Two agents that edit different files each hold a current file sha, so both writes land, yet the second agent planned its edit against an old package. The If-Match header closes that gap, and a stale value returns 409 format_package_sha_mismatch.
Where the value comes from
The package sha is commit.tree.sha from your last write. It is also the package_sha field on the Format record, and POST /v1/formats returns it when you create a Format. It is a bare 40-character hex string, not an HTTP ETag in quotes. Formats published before package_sha existed ignore the header instead of returning a 409 that no one could satisfy.
The two checks side by side
The table compares the two guards. Both come from the Contents docs, as of 2026-10-09.
| Guard | Sent as | Protects against | Error when stale |
|---|---|---|---|
File sha | Field in the body | Two writers on the same file | 409 format_content_sha_mismatch |
File sha missing | (not sent) | Overwriting an existing path blindly | 409 format_content_sha_required |
| Package sha | If-Match header | Two writers on different files | 409 format_package_sha_mismatch |
A safe edit loop
Use the loop below for each agent turn that edits a Format. It needs one read and one write when nothing has moved, and one extra read when something has.
- Read the whole package with
GET …/contents?recursive=1. Each row carries itsshaand body. - Plan the edit and build a
fileschange set. Paths you do not name are kept, so you cannot delete a file this way. PUT …/contentswithIf-Matchset to the package sha and the currentshaon each existing path.- On
409 format_package_sha_mismatch, readerror.details.package_sha, re-read the package, redo the plan and write again. - Keep the new
commit.tree.shafor the next write.
What the guard does not do
A change to a package has no effect on a run that already started. Each run reads the package it started with, and the receipt's format.version records which version ran. The API has no revert, blame or history browsing, and no clone URL, so keep your own copy of anything you may need to restore. A 503 format_git_unavailable means nothing was saved, so retry the write.
Reading the 409 in code
The 409 is part of normal operation with several writers, so write the retry on purpose. The error carries the current package sha in error.details.package_sha, which saves one request, but the contents of the package changed too, so you must re-read the files and rebuild your change set. Do not just resend the old body with the new header: that would defeat the check, and the per-file sha values would probably mismatch anyway.
Limit the retries. If two agents keep colliding, one of them is editing a file the other also needs, and the better fix is to give each agent its own files, for example one references/ note for each. The author of each commit is always the owner of the key, whatever author the body names.
Sources
Related posts
More in Formats
- Share a Format with another workspace: grants, accept and the 404
A Format owner can share a team Format with another workspace by grant. The other workspace accepts, calls it with its own key, and pays its own bill.
- Format input vs instruction: where scraped product copy should go
Put scraped or customer text in the input object, not in instruction. Sume writes input to a file and marks it as data. Limits: 64 keys, 2 MiB, 4000 characters.
- Cancel a Format run: cancel_effect, no_op and what you still pay
POST cancel on a Format run is idempotent. cancel_effect says canceled or no_op. You pay for generation done before the cancel, and no webhook is sent.
- Format run cap for 25 Nano Banana 2.1 images: $5.00 at 4K
A Format run that makes 25 Nano Banana 2.1 images costs $2.50 at 1K, $3.75 at 2K and $5.00 at 4K. How to set generation_spend_cap_usd and the Format default.
Written by Sume