# Uploads

Single-use, size-bounded Safeput uploads and their trust boundary.

Safeput keeps file bytes out of your application server while placing them in
private quarantine storage.

## Upload flow

1. Your trusted backend calls `POST /v1/uploads` with a project API key.
2. Safeput reserves account-wide capacity and returns a 10-minute, single-use target.
3. Your client sends a known-length file to that target with `PUT` and every returned header.
4. Your backend can read the record with `GET /v1/uploads/:id`.

Use a unique `Idempotency-Key` for each logical upload attempt. Retrying the
same request is safe; changing the body while reusing the key is rejected.

## Trust boundary

Treat every uploaded object as untrusted. A successful `PUT` (`204`) means the
Worker accepted exactly the declared bytes and durably published the private
object for inspection. It does not mean the file has passed inspection.

The queue consumer validates the object metadata and runs the isolated scanner.
Only a later `clean` status may make a file eligible for retrieval.

## Limits

- Declared size: 1 byte through 52,428,800 bytes (50 MiB)
- Upload URL lifetime: 10 minutes
- Started transfer deadline: 5 minutes
- Storage: private R2; clients receive no R2 write credential
- API key scope: one project and one `test` or `live` environment

Targets use a scoped `Authorization` header; never replace it with the project
key or put it in a URL/log. A file or browser Blob supplies Content-Length;
omitting it returns `411`. Content-Encoding is not accepted. Short bodies,
oversized bodies and size mismatches cannot publish partial objects.

Once a transfer starts, its capability is consumed, even if the connection fails
or the file is later rejected/deleted. Poll the existing upload after an uncertain
response. Do not blindly create a duplicate: publication may be recovering. Use
a new Idempotency-Key only when a new logical attempt is appropriate.

Admission limits are shared across an account's projects and keys, including test
keys. `429 admission_limit_exceeded` means its file, byte, outstanding or issuance
budget is exhausted. These operational limits are separate from scanned-byte
billing; a failed pre-scan transfer has zero billable scanned bytes.

See the [API reference](/docs/api-reference/) for request and response fields.
