Skip to content

Upload a file

uploadFile

PUT /api/v1/shares/{share_id}/files

Scope: files.writeRate cost: 5

Upload a new file: the request body is the file

The bytes stream to the share as they arrive, at any size; Content-Length is required, so a share without room refuses before the first byte. New files only, said as HTTP says it: send If-None-Match: * (required, 428 without it). Something already called that is never replaced: the default answers 412 precondition-failed; on_conflict=rename picks the first free name instead, so read the answer’s path (and Location). If-Match names a file to replace, which this operation never does: sending it is refused (400); replace a file with overwriteFile. A person may have only a few uploads in flight through your app at once (429 with Retry-After beyond that). On a share that offers it, an upload session (createUploadSession) sends the bytes straight to the storage instead. Send an Idempotency-Key: the same upload sent again with it (the same path, query and bytes) answers the first answer and writes nothing; other bytes under the same key are refused (422).

Terminal window
curl -X PUT "$FOLDR/api/v1/shares/$SHARE_ID/files?path=%2FReports%2Fq3.pdf" \
-H "Authorization: Bearer $TOKEN" \
-H "If-None-Match: *" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/octet-stream" \
--data-binary @./file

$FOLDR is your site’s address (for example https://files.example.com) and $TOKEN is an access token. See Getting access.

NameInTypeRequiredDescription
share_idpathstringYesThe share’s id.
pathquerystringYesThe new file’s full path in the share, its name last. The folder it goes in must exist.
on_conflictquerystring, one of fail, renameNoWhat to do when something is already called that: “fail” (the default) or “rename”. Nothing is ever replaced.
If-None-MatchheaderstringYes”*”: create only, never replace (RFC 9110). Required; no other value is taken.
Idempotency-KeyheaderstringYesA value of your own, 1 to 255 visible ASCII characters, new for each new upload. Sending the same upload again (the same path, query and bytes) with the same key within 24 hours answers the first answer and writes nothing (the body is read again, to be sure it is the same); with a different request, other bytes included, it is refused (422).

The file’s bytes, exactly Content-Length of them.

application/octet-stream, required: string (binary)

StatusMeaningBody
201The file, at its canonical path.application/json: Item
HeaderDescription
ETagThe entity tag of what was answered. Send it back as If-None-Match for a 304 while it is unchanged, or as If-Match to change it only while it is unchanged.
LocationWhere to read what was made.

Item:

FieldTypeDescription
share_idstring (uuid)Required. The id of the share the item is in.
pathstringRequired. The canonical path: as the storage holds it (its case), Unicode NFC, always starting with ”/”. ”/” is the top of the share. Send this form back in later calls.
namestringRequired. The item’s name (the last segment of its path; the share’s name for the top).
kindstring, one of file, folderRequired. file or folder.
sizeinteger or nullRequired. Size in bytes; null for a folder, and null when the item has no fixed byte size (a native Google document, exported on download: see export).
modifiedstring or null (date-time)Required. Last modified (RFC 3339, UTC), where the storage reports it.
mime_typestring or nullRequired. The item’s own media type, as the storage reports it or guessed from the name; null for a folder. A native Google document has Google’s own type here (application/vnd.google-apps.document for a Doc); export says what it downloads as.
exportExport or nullRequired. For a native Google document that can be downloaded, what content delivers: its media type and the extension the download is named with. Null for everything else, including a native Google type that cannot be downloaded (a Form: content answers 409 not-downloadable).
etagstring or nullRequired. The item’s entity tag: the storage’s own where it has one, else a weak tag over size and modified time (over modified time alone for a native Google document, whose export is made afresh each time). Null on a search hit, which comes from the index; GET …/item for the live one.
read_onlybooleanRequired. True when the person cannot change things in this share.
checked_outCheckout or nullRequired. Who has the item checked out, or null. Null on a search hit.

Example:

{
"share_id": "0191f2c4-8a1e-7b52-9d3a-5c1e0b7a4d21",
"path": "string",
"name": "string",
"kind": "file",
"size": 1,
"modified": "2026-11-04T09:30:00Z",
"mime_type": "string",
"export": {
"mime_type": "string",
"extension": "string"
},
"etag": "string",
"read_only": false,
"checked_out": {
"by": {},
"since": "2026-11-04T09:30:00Z"
}
}

Every error is an application/problem+json document (ProblemDetails); branch on its type or code. See Errors.

StatusProblem typesWhen
400invalid-path
invalid-request
One of: Invalid path (invalid-path); Invalid request (invalid-request).
401invalid-tokenInvalid token (invalid-token).
403insufficient-scope
write-not-allowed
One of: Insufficient scope (insufficient-scope); Not allowed for this app (write-not-allowed).
404not-foundNot found (not-found).
409ambiguous-path
conflict
not-a-folder
storage-account-not-connected
One of: Ambiguous path (ambiguous-path); Conflict (conflict); Not a folder (not-a-folder); Storage account not connected (storage-account-not-connected).
412precondition-failedPrecondition failed (precondition-failed).
413too-largeToo large (too-large).
422idempotency-key-reusedIdempotency key reused (idempotency-key-reused).
428precondition-requiredPrecondition required (precondition-required).
429daily-write-limit
rate-limited
One of: Daily write limit reached (daily-write-limit); Too many requests (rate-limited).
500internal-errorInternal error (internal-error).
503share-unavailable
share-unreachable
write-failed
One of: Share unavailable (share-unavailable); Share unreachable (share-unreachable); Write failed (write-failed).