Request files

Request files

The files your organization holds for a request come from two directions and live in one listing: kept copies of the person’s files (origin: kept — taken under a keep verb, one version per keep) and files you attached yourself (origin: added — a signed agreement, a letter, a scan of your own). Both are rows of your organization’s own vault carrying the request they belong to; the person sees neither, because no organization → person direction exists. Every route on this page needs request_manage; writes also need the file permission named.

List the request’s files

GET /org/v1/requests/{requestId}/files

One row per file, at the head of its versions. Nothing is decrypted — a file’s readable part is its metadata — and each row’s downloadUrl is the plane’s own version-pinned route for its bytes.

{
  "files": [
    {
      "fileId": "f21a…",
      "origin": "kept",
      "orgVersion": 2,
      "adoptionId": "5d2a…",
      "sourceVersion": 3,
      "slotId": "8e6d…",
      "label": "Passport scan",
      "fileName": "passport.pdf",
      "fileSize": 184233,
      "mimeType": "application/pdf",
      "writtenBy": "2c1e…",
      "writtenAt": "2026-09-02T09:00:00Z",
      "downloadUrl": "/org/v1/requests/6f9b…/files/f21a…/download?version=2"
    },
    {
      "fileId": "a77c…",
      "origin": "added",
      "orgVersion": 1,
      "label": "Signed subscription agreement",
      "fileName": "agreement.pdf",
      "fileSize": 302118,
      "mimeType": "application/pdf",
      "writtenBy": "2c1e…",
      "writtenAt": "2026-09-05T14:20:00Z",
      "downloadUrl": "/org/v1/requests/6f9b…/files/a77c…/download?version=1"
    }
  ]
}
  • A kept row names the keep that produced this version (adoptionId) and the person’s version it was taken at (sourceVersion); its versions are the person’s versions as kept, and your organization never writes into that history.
  • An added row names neither — nothing of the person’s was taken — and usually no slotId either: a request whose manifest never asked for files still accepts attachments. When slotId is present it names a slot of the request’s own manifest version.
  • mimeType is the type the bytes are, detected at write time — never the declared one. label is absent when nobody gave one; fall back to fileName. writtenBy is the member who attached, replaced or renamed the version, or the member a keep is booked to.

Attach a file of your own

POST /org/v1/requests/{requestId}/files
Content-Type: multipart/form-data

file=@agreement.pdf  label="Signed subscription agreement"  [slotId=<slot of this manifest version>]

Stores the file in your organization’s vault, tagged as your own and naming this request, at orgVersion: 1. No adoption row, receipt or proposal is written, because nothing of the person’s was taken. Works on every request you hold, in any state, whatever its manifest asks for. Needs file_create. The bytes are capped per file (32 MiB by default — 413 too_large above it, nothing written); a cold organization key answers 423 org_sealed. Answers the new RequestFile row.

Replace the bytes

PUT /org/v1/requests/{requestId}/files/{fileId}
Content-Type: multipart/form-data

file=@agreement-v2.pdf  expectedOrgVersion=1

The next version of an added file, with its own object and its own key, so anything pinned to the older version keeps reading the bytes it always did. Needs file_update. Pinned to what you saw: expectedOrgVersion is the orgVersion you read, and a stale one answers 409 version_conflict having written nothing — another member replaced or renamed it first; re-read /files and decide again. A kept file is refused (403 adoption_refused).

Rename

PATCH /org/v1/requests/{requestId}/files/{fileId}
Content-Type: application/json

{ "label": "Subscription agreement (countersigned)" }

A metadata-only version: the same bytes and the same object under a new label, plus a row saying who renamed it and when. An empty label clears it. Needs file_update; a kept file is refused for the same reason as on PUT.

Delete

DELETE /org/v1/requests/{requestId}/files/{fileId}

Every version’s row, then every version’s bytes — this is the only chance to clean the objects up. Needs file_delete. A kept copy is refused (403 adoption_refused): the keep it records is your organization’s own account of what it took from the person.

{ "fileId": "a77c…" }

History

GET /org/v1/requests/{requestId}/files/{fileId}/history

Every version of one file, newest first, in the same row shape as the listing: the keeps of a kept file, each naming its adoption and the person’s version it was taken at, or the replacements and renames of a file you attached, each naming the member who wrote it. Each row’s downloadUrl serves that version’s own bytes.

Download

GET /org/v1/requests/{requestId}/files/{fileId}/download
GET /org/v1/requests/{requestId}/files/{fileId}/download?version=2

Streams one version of the file — the head when version is absent — as an attachment (Content-Disposition, the file’s own Content-Type and Content-Length), decrypted with your organization’s key. The route re-proves the whole chain before a byte moves: the request is yours and the file lives in your vault carrying this request, so another request’s file under the same organization reads as 404 not_found, exactly like an unknown id or a version the file does not have.

A kept file is reachable here and through the adoption-scoped /copies/{adoptionId}/download, which serves the version that adoption kept; an attachment has no adoption and is served only here.