POST /v1/uploads returns a short-lived upload link, and a PUT sends the bytes to it. The upload_key from the first answer is what you pass in references when you create a character.
1. Ask for an upload link
POST /v1/uploads · scope chars:write
r2Key holds the same value as upload_key and is deprecated; read upload_key. thumb is a second link the dashboard uses for previews; it is null unless you send thumbBytes, which you don’t need.
2. Send the bytes
PUT the file to uploadUrl with exactly the headers from headers, before expiresAt (10 minutes):
content-type, a file of another size, or a late request is refused, and you ask for a new link. No API key goes on the PUT: the link carries its own permission, so treat it as a secret until it expires.
3. Use the upload key
Pass eachupload_key in references, as a string or as { "upload_key": "..." }:
PUT before you create the character. The API does not check that the bytes arrived: in production a reference that was never sent fails the build, and you get char.failed; sandbox does not notice at all.
What you can upload
A character takes at most 20 files across face, body and voice, so 12 face and 12 body photos together are refused. About 30 seconds of clear speech is enough for a voice. For which photos make a good character, see Finetuning.
Who can use an upload
An API key acts as the person who created it, and an upload belongs to that person. Uploads made with one key can be used by any key that same person created, in either environment. A key created by a teammate cannot use them, and the character request answers422 naming the reference, for example references.face[0] is not one of your uploads.
Rotating a key from Settings makes the new key act as whoever rotated it. If a teammate rotates your key, upload the files again with the new key.
Storage
Uploads are stored files, with sandbox keys too, and count toward your team’s storage allowance. When an upload would go over it,POST /v1/uploads answers 409 with the code storage_limit_reached.
Upload a folder of photos
This uploads every.jpg in a folder and prints the keys as a JSON array, ready for references.face. It needs jq.
Errors
See Errors for the shape of an error body.