Skip to main content

patch /upload/<uuid>

Patch upload

Appends the next chunk to a resumable upload created by POST /<uuid>/file or PUT /<uuid>/file.

Every request requires Content-Type: application/partial-upload and an Upload-Offset header equal to the upload’s current offset - the value from the previous response’s Upload-Offset header (or HEAD’s, if reconnecting after a restart). An offset that does not match fails with 409 Conflict, listing the expected and provided values in the problem’s details, so the client can re-synchronize.

Set Upload-Complete: ?1 on the chunk that finishes the upload. Its body may be empty - a zero-length chunk with Upload-Complete: ?1 is a valid way to complete an upload without transmitting further data, and behaves like HEAD /upload/<uuid> otherwise, since zero-length chunks are always accepted regardless of the configured minimum chunk size.

Once the final chunk is stored, all chunks are merged and attached as the target element’s file (as if uploaded via PUT /<uuid>/file), and the resumable upload itself ceases to exist - subsequent requests against its URL respond with 404.

Every unfinished upload has a limited lifetime, reported in the max-age field of its Upload-Limit header and reflected in its Expires header. Once that time passes, appending further chunks fails with 410 Gone; the upload has to be recreated from scratch via POST or PUT.

This endpoint does not support If-Match or If-None-Match; unlike the element endpoints, requesting it with either header has no effect.

On the chunk that sets Upload-Complete: ?1, a Repr-Digest header (or Content-Digest, as a fallback) can be sent with the SHA-256 digest of the complete, reassembled file, in RFC 9530 structured-field format (sha-256=:[base64]:), the same as for a direct upload on POST /<uuid>/file. A mismatch fails with 400 Bad Request; the uploaded chunks are discarded and the upload’s target element keeps whatever file it had before (if any).

Request

curl -X PATCH \
-H 'Authorization: Bearer secret-token:PIPeJGUt7c00ENn8a5uDlc' \
-H 'Content-Type: application/partial-upload' \
-H 'Upload-Complete: ?1' \
-H 'Upload-Offset: 5242880' \
-d '<binary chunk data>' \
https://api.example.com/upload/7b80b203-2b82-40f5-accd-c7089fe6114e
import requests
url = "https://api.example.com/upload/7b80b203-2b82-40f5-accd-c7089fe6114e"
payload = "<binary chunk data>"
headers = {
"Authorization": "Bearer secret-token:PIPeJGUt7c00ENn8a5uDlc",
"Content-Type": "application/partial-upload",
"Upload-Offset": "5242880",
"Upload-Complete": "?1"
}
response = requests.patch(url, data=payload, headers=headers)
print(response.json())
const url = 'https://api.example.com/upload/7b80b203-2b82-40f5-accd-c7089fe6114e';
const options = {
method: 'PATCH',
headers: {
Authorization: 'Bearer secret-token:PIPeJGUt7c00ENn8a5uDlc',
'Content-Type': 'application/partial-upload',
'Upload-Offset': '5242880',
'Upload-Complete': '?1'
},
body: '<binary chunk data>'
};
try {
const response = await fetch(url, options);
const data = await response.json();
console.log(data);
} catch (error) {
console.error(error);
}
<?php
$client = new \GuzzleHttp\Client();
$response = $client->request('PATCH', 'https://api.example.com/upload/7b80b203-2b82-40f5-accd-c7089fe6114e', [
'body' => '<binary chunk data>',
'headers' => [
'Authorization' => 'Bearer secret-token:PIPeJGUt7c00ENn8a5uDlc',
'Content-Type' => 'application/partial-upload',
'Upload-Complete' => '?1',
'Upload-Offset' => '5242880',
],
]);
echo $response->getBody();

Response

204 · No Content

The chunk was stored successfully.
Specific meaning depends on Upload-Complete:

  • ?0: The chunk was appended, and the upload remains open for further chunks. Its new state is returned in the response headers.
  • ?1: The chunk was appended and the upload is now complete - the uploaded chunks were merged and attached as the target element’s file, and the resumable upload no longer exists.

400 · Bad Request

A provided property has the wrong type or fails a validation rule.

401 · Unauthorized

The provided token is either invalid or expired.

404 · Not Found

No resumable upload with this id exists, it already completed, it belongs to a different user, or the current user has since lost UPDATE access to the upload’s target element.

409 · Conflict

The Upload-Offset in the request does not match the upload’s current offset, or the appended chunk would exceed the upload’s declared Upload-Length.

410 · Gone

The upload has expired and its data has already been discarded.

response has no body

Access-Control-Allow-Headers: Authorization, Origin, X-Requested-With, Content-Type, Accept, Access-Control-Request-Method
Access-Control-Allow-Methods: GET, HEAD, POST, OPTIONS, PUT, PATCH, DELETE, PROPFIND, PROPPATCH, MKCOL, COPY, MOVE, LOCK, UNLOCK
Access-Control-Allow-Origin: *
Access-Control-Expose-Headers: ETag, Location
Allow: GET, HEAD, POST, OPTIONS, PUT, PATCH, DELETE, PROPFIND, PROPPATCH, MKCOL, COPY, MOVE, LOCK, UNLOCK
Cache-Control: no-cache, private
Upload-Complete: ?0
Upload-Offset: 5242880
Upload-Length: 20971520
Upload-Limit: max-age=10800, max-size=10737418240, min-append-size=5242880, max-append-size=105906176
Expires: Sat, 22 Aug 2026 21:22:07 GMT
X-Powered-By: Ember Nexus API

no schema available

{
"type": "https://api.example.com/error/400/bad-content",
"title": "Bad content",
"status": 400,
"detail": "Endpoint expects property 'newPassword' to be password which is not identical to the old password, got string '<redacted>'."
}
Access-Control-Allow-Headers: Authorization, Origin, X-Requested-With, Content-Type, Accept, Access-Control-Request-Method
Access-Control-Allow-Methods: GET, HEAD, POST, OPTIONS, PUT, PATCH, DELETE, PROPFIND, PROPPATCH, MKCOL, COPY, MOVE, LOCK, UNLOCK
Access-Control-Allow-Origin: *
Access-Control-Expose-Headers: ETag, Location
Allow: GET, HEAD, POST, OPTIONS, PUT, PATCH, DELETE, PROPFIND, PROPPATCH, MKCOL, COPY, MOVE, LOCK, UNLOCK
Cache-Control: no-cache, private
X-Powered-By: Ember Nexus API
{
"$schema": "https://spec.openapis.org/oas/3.0/schema/2021-09-28#/definitions/Schema",
"type": "object",
"description": "RFC 7807 Problem Details object.",
"properties": {
"type": {
"type": "string",
"format": "uri"
},
"title": {
"type": "string"
},
"status": {
"type": "integer"
},
"detail": {
"type": "string"
}
}
}
{
"type": "https://api.example.com/error/401/unauthorized",
"title": "Unauthorized",
"status": 401,
"detail": "Authorization for the request failed due to possible problems with the token (incorrect or expired), password (incorrect or changed), the user's unique identifier, or the user's status (e.g., missing, blocked, or deleted)."
}
Access-Control-Allow-Headers: Authorization, Origin, X-Requested-With, Content-Type, Accept, Access-Control-Request-Method
Access-Control-Allow-Methods: GET, HEAD, POST, OPTIONS, PUT, PATCH, DELETE, PROPFIND, PROPPATCH, MKCOL, COPY, MOVE, LOCK, UNLOCK
Access-Control-Allow-Origin: *
Access-Control-Expose-Headers: ETag, Location
Allow: GET, HEAD, POST, OPTIONS, PUT, PATCH, DELETE, PROPFIND, PROPPATCH, MKCOL, COPY, MOVE, LOCK, UNLOCK
Cache-Control: no-cache, private
X-Powered-By: Ember Nexus API
{
"$schema": "https://spec.openapis.org/oas/3.0/schema/2021-09-28#/definitions/Schema",
"type": "object",
"description": "RFC 7807 Problem Details object.",
"properties": {
"type": {
"type": "string",
"format": "uri"
},
"title": {
"type": "string"
},
"status": {
"type": "integer"
},
"detail": {
"type": "string"
}
}
}
{
"type": "https://api.example.com/error/404/not-found",
"title": "Not Found",
"status": 404,
"detail": "Requested element was not found."
}
Access-Control-Allow-Headers: Authorization, Origin, X-Requested-With, Content-Type, Accept, Access-Control-Request-Method
Access-Control-Allow-Methods: GET, HEAD, POST, OPTIONS, PUT, PATCH, DELETE, PROPFIND, PROPPATCH, MKCOL, COPY, MOVE, LOCK, UNLOCK
Access-Control-Allow-Origin: *
Access-Control-Expose-Headers: ETag, Location
Allow: GET, HEAD, POST, OPTIONS, PUT, PATCH, DELETE, PROPFIND, PROPPATCH, MKCOL, COPY, MOVE, LOCK, UNLOCK
Cache-Control: no-cache, private
X-Powered-By: Ember Nexus API
{
"$schema": "https://spec.openapis.org/oas/3.0/schema/2021-09-28#/definitions/Schema",
"type": "object",
"description": "RFC 7807 Problem Details object.",
"properties": {
"type": {
"type": "string",
"format": "uri"
},
"title": {
"type": "string"
},
"status": {
"type": "integer"
},
"detail": {
"type": "string"
}
}
}
{
"type": "https://api.example.com/error/409/conflict",
"title": "Conflict",
"status": 409,
"detail": "Offset from request does not match offset of resource.",
"expected-offset": 20971520,
"provided-offset": 10485760
}
Access-Control-Allow-Headers: Authorization, Origin, X-Requested-With, Content-Type, Accept, Access-Control-Request-Method
Access-Control-Allow-Methods: GET, HEAD, POST, OPTIONS, PUT, PATCH, DELETE, PROPFIND, PROPPATCH, MKCOL, COPY, MOVE, LOCK, UNLOCK
Access-Control-Allow-Origin: *
Access-Control-Expose-Headers: ETag, Location
Allow: GET, HEAD, POST, OPTIONS, PUT, PATCH, DELETE, PROPFIND, PROPPATCH, MKCOL, COPY, MOVE, LOCK, UNLOCK
Cache-Control: no-cache, private
X-Powered-By: Ember Nexus API
{
"$schema": "https://spec.openapis.org/oas/3.0/schema/2021-09-28#/definitions/Schema",
"type": "object",
"description": "RFC 7807 Problem Details object.",
"properties": {
"type": {
"type": "string",
"format": "uri"
},
"title": {
"type": "string"
},
"status": {
"type": "integer"
},
"detail": {
"type": "string"
}
}
}
{
"type": "https://api.example.com/error/410/gone",
"title": "Gone",
"status": 410,
"detail": "Requested resource is no longer available and is expected to soon be permanently deleted."
}
Access-Control-Allow-Headers: Authorization, Origin, X-Requested-With, Content-Type, Accept, Access-Control-Request-Method
Access-Control-Allow-Methods: GET, HEAD, POST, OPTIONS, PUT, PATCH, DELETE, PROPFIND, PROPPATCH, MKCOL, COPY, MOVE, LOCK, UNLOCK
Access-Control-Allow-Origin: *
Access-Control-Expose-Headers: ETag, Location
Allow: GET, HEAD, POST, OPTIONS, PUT, PATCH, DELETE, PROPFIND, PROPPATCH, MKCOL, COPY, MOVE, LOCK, UNLOCK
Cache-Control: no-cache, private
X-Powered-By: Ember Nexus API
{
"$schema": "https://spec.openapis.org/oas/3.0/schema/2021-09-28#/definitions/Schema",
"type": "object",
"description": "RFC 7807 Problem Details object.",
"properties": {
"type": {
"type": "string",
"format": "uri"
},
"title": {
"type": "string"
},
"status": {
"type": "integer"
},
"detail": {
"type": "string"
}
}
}

Request Parameters

uuid

required

pathstringuuid

Id of the resumable upload, as returned in the Location header of the request that created it.

Request Header

Authorization

optional

Bearer token for user authentication. If omitted, the request is processed as an anonymous user; if provided, the associated user account is used.

Upload-Offset

required

Byte offset the appended chunk starts at. Must match the upload's current Upload-Offset exactly, otherwise the request fails with 409 Conflict.

Upload-Complete

optional

?1 marks this as the final chunk, completing the upload; omitted or ?0 keeps the upload open for further chunks.

Repr-Digest

optional

Cryptographic digest of the complete (reassembled) file. Only read on the chunk that sets Upload-Complete: ?1, and checked against the merged file once assembled. Only sha-256 is supported. A mismatch fails with 400 Bad Request; the uploaded chunks are discarded and the upload's target element keeps its previous file, if any. Content-Digest is accepted as a fallback if Repr-Digest is absent.

Request Body

no example available

{
"type": "string",
"format": "binary"
}

Response Header

Upload-Complete

always

Structured boolean indicating whether the upload is finished: ?1 for complete, ?0 for still in progress.

Upload-Offset

always

Number of bytes already stored for the resumable upload. On a request, it must match the upload's current offset exactly; on a response, it reflects the offset after the request was processed.

Upload-Length

optional

Total number of bytes the finished file will have. Optional - omitted when the total size is not known ahead of time.

Upload-Limit

always

Structured field listing the server's limits for this upload: max-age (seconds an unfinished upload is kept alive since its first request), max-size (maximum total file size in bytes), min-append-size (minimum size in bytes for a non-final chunk) and max-append-size (maximum size in bytes per chunk).

Expires

always

Point in time after which the unfinished resumable upload is discarded, if it is not completed before then.

Access-Control-Allow-Headers

always

Specifies which HTTP headers can be used during a cross-origin request, returned in preflight responses.

Access-Control-Allow-Methods

always

Indicates which HTTP methods are permitted for cross-origin requests, typically returned in preflight responses.

Access-Control-Allow-Origin

always

Specifies which origin(s) may access the resource.

Access-Control-Expose-Headers

always

Lists the response headers that browsers are allowed to access from JavaScript beyond the default restricted set.

Allow

always

Lists the HTTP methods supported by the endpoint; returned alongside a 405 Method Not Allowed response.

Cache-Control

always

Directives controlling caching behavior for the response.

X-Powered-By

always

Identifies the underlying API implementation.