Start a file upload on a drive item
POSThttps://
Step 1 of 3.
Access
- Token scope
- api:
write: orapi:project_drive write: orapi:customer_drive write: orapi:organization_drive write: user_drive Without it:
403 insufficient-token-scope. - Permission
- project_drive::
create Without it:
403 insufficient-permission. - Rate cost
- 1 unit, write bucket. A write costs the same whatever
viewyou ask for. - Also
- Accepts
Idempotency-Key. Never cached:private, no-store. Unknown body keys are refused, never dropped.
Both are checked, scope first. A token never does more than its user. How access is checked
Returns an upload ticket whose upload_url you PUT the raw bytes to, after which you POST the ticket's id as upload_id to drive-items/{drive_item}/files to turn those bytes into a file. The bytes never travel through this API: /api/v2 accepts application/json and nothing else, so the upload target is a separate signed route. The file always lands in the document's attachments collection (content belongs to the editor and file is the document's own single payload, so neither is writable here), and the collection, the storage location and the ticket lifetime are all chosen by the server. The declared size is not a bound: the server counts what arrives and refuses anything over 10 MB. The declared permission is project_drive::create; customer_drive, organization_drive and user_drive apply by owner type, and the record rule requires edit access: read access to a document is not enough to attach to it, which is deliberately stricter than plnnrly. An archived document is 404. The ticket is yours alone, is single-use, expires in 30 minutes, and can be deleted with DELETE uploads/{upload} while unregistered.
Path parameters
Query parameters
Unknown parameters are refused with 400, naming the parameter.
Request headers
A correlation id of your own. It is recorded in our logs next to the server's request id but never returned: the response header always carries the server's id. A value that does not match the pattern is discarded silently, never rejected.
Request body
Unknown keys are refused with 422, naming the key.
The sanitized file name the file will carry. Sent when the ticket is created; control characters are stripped and #, /, \ and spaces become hyphens, so what you read back can differ from what you sent.
The size you declared when creating the ticket. A declaration, not a bound: the server counts the bytes it actually receives.
Response 201
Returns a Upload in the detail view.
Headers
ETag- A strong validator of exactly this representation.
Idempotency-Replayedtruewhen this response was replayed from a storedIdempotency-Keyrather than produced by a new write.Location- The absolute URL of the created or queued resource.
RateLimit- The IETF
httpapi-ratelimit-headersform of your remaining allowance on the tightest bucket this request touched. RateLimit-Policy- The ceiling and window of that bucket, as
<limit>;w=<seconds>. X-Request-Id- The server-generated ULID correlating this response with our logs.
Every response also carries Cache-Control, Vary, X-Content-Type-Options, X-Plnnrly-Api-Version, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset. Headers and versioning
The work item this ticket attaches to, or null when it targets a drive item.
The drive item this ticket attaches to, or null when it targets a work item.
The media collection the file will land in. Always attachments: it is chosen by the route, and no request field can change it.
The sanitized file name the file will carry. Sent when the ticket is created; control characters are stripped and #, /, \ and spaces become hyphens, so what you read back can differ from what you sent.
The size you declared when creating the ticket. A declaration, not a bound: the server counts the bytes it actually receives.
The optional SHA-256 you declared when creating the ticket. When present the server refuses bytes that do not match it.
The number of bytes that actually arrived, measured by the server. Null until the upload lands.
The SHA-256 the server computed over the bytes it received. Null until the upload lands.
The media type detected from the received bytes. Detected, never declared: the API accepts no content type when creating the ticket and ignores the Content-Type of the upload itself.
Where the ticket is in its lifecycle.
pendinguploadedcommittingcommittedexpiredrevoked
New values may be added.
Where to PUT the file. An opaque absolute URL: send the raw file as the body of a PUT whose only header is your Authorization bearer token. No Content-Type is read and the body is stored exactly as sent, so a form envelope would become part of the file. Present only while status is pending.
The deadline for the PUT. After it the ticket is dead and a new one must be created.
The deadline for the registration, set when the bytes land and measured from that moment, so a slow upload does not eat into the time you have to register. Null until the upload lands.
When the bytes became a file. Null until they do.
The id of the file this ticket became. Null until it is registered, and set exactly once.
Errors
415 Unsupported media type.
422 Unprocessable content.
7 errors every operation can return: 400, 401, 404, 406, 429, 500, 503
400 Bad request.
idempotency-key-requiredinvalid-cursorinvalid-query-parametermalformed-requestunsupported-parameter
401 Unauthenticated.
404 Not found.
406 Not acceptable.
429 Too many requests.
500 Server error.
503 Service unavailable.
This path allows POST. Anything else answers 405.
Something wrong or missing on this page? Tell us