--- title: Files description: Manage Environment and Persona files using the public API. --- # Files All routes below use `/v1/projects/{projectId}/files`. Project UUIDs and file UUIDs are required. Credentials inherit current project permissions and need `read` or `write` scope for the corresponding operations. | Method | Suffix | Purpose | | ------ | ----------------------------------- | ---------------------------------------------------------------------- | | GET | `?scope=environment&ownerId=UUID` | List ready files for an Environment; use `scope=persona` for a Persona | | POST | empty | Initiate a new upload | | GET | `/{id}` | Get metadata | | PATCH | `/{id}` | Edit `name` and/or `description` | | DELETE | `/{id}` | Remove from future executions | | POST | `/{id}/uploads` | Initiate replacement | | POST | `/{id}/uploads/{uploadId}/complete` | Verify and publish uploaded bytes | | GET | `/{id}/preview?page=1&offset=0` | Bounded preview | | GET | `/{id}/download` | Original bytes as an authenticated attachment | | GET | `/runs/{runId}` | Frozen manifest, content availability and read/upload usage | | GET | `/runs/{runId}/{id}/preview` | Preview the exact historical version | | GET | `/runs/{runId}/{id}/download` | Download the exact historical version | ## Upload Initiate with the file's byte size and lowercase hexadecimal SHA-256: ```json { "scope": "environment", "ownerId": "11111111-1111-4111-8111-111111111111", "filename": "invoice.pdf", "mimeType": "application/pdf", "size": 12345, "checksum": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef", "name": "Sample invoice", "description": "Use for invoice import tests." } ``` The response contains `fileId`, `uploadId`, `url`, `method: "PUT"`, required `headers`, and `expiresIn: 600`. PUT the original bytes directly to that URL using the returned headers, without your Smoketest API key. Then call the completion endpoint with your API key. The file becomes ready only after size and checksum verification. The signed URL is temporary and must not be stored in Test instructions. For replacement, send the same content metadata to `/{id}/uploads`, omitting owner scope. Ownership cannot change. Replacements preserve the file ID and publish a new immutable version. One replacement can be pending at a time. An expired/abandoned reservation is reclaimed automatically; initiate a new upload after expiry. Limits: 25 MiB per file and 5 GiB of current files per project. Replacements reserve positive growth; old versions needed by unfinished work do not count against the current-file quota. Quota and validation errors use 400; conflicting/expired reservations use 409. Missing/out-of-scope Test references reject dispatch with 422. Storage failures use 503. ## Preview and history Preview responses contain a `kind`, bounded `text` and/or base64 JPEG `image`, optional `page`/`pages`, `offset`/`nextOffset`, and `truncated`. PDF pages are one-based. Text offsets are UTF-8 bytes; PDF text offsets address the selected page's extracted text. Follow `nextOffset` on the same page. `unsupported` or `unavailable` includes a reason and leaves original bytes uploadable. Historical manifests never substitute current bytes for reclaimed versions. `contentsAvailable: false` means preview/download is no longer available. Run-file routes also enforce Run/source visibility. File references use `[label](smoketest-file:UUID)` in Test Markdown. Individual Test create/get/update responses include `fileDiagnostics` for missing/out-of-scope references. Incomplete Tests can be saved; execution validates again before dispatch and charging. No new MCP tools are included. Run-file history may include `scope: "ticket"` and ticket provenance (provider, ticket identity, and known description/comment origins). Management endpoints continue to accept only Environment and Persona scopes. Usage distinguishes metadata disclosure (`list`), inspection (`read`), and upload (`upload`), with timestamps and inspection evidence IDs when recorded. No public Attempt-management interface is introduced.