DocumentContent - Huddle/huddle-apis GitHub Wiki
Retrieving Document Content
Documents have binary content associated with them. If a document has binary content, it will advertise a link with a rel value of content. The title attribute of the link gives the filename, and the type attribute gives the MIME type.
If a document has multiple versions, the content link points to the latest active version (not deleted, and with content successfully uploaded). If no binary content exists for the document, the server returns 404 Not Found.
Operations
| Method | Path | Purpose | Details |
|---|---|---|---|
GET, HEAD |
/files/documents/{documentId}/versions/latest/content |
Get latest document content (raw or converted) | Jump |
GET, HEAD |
/files/documents/{versionId}/content |
Get specific version content (raw or converted) | Jump |
GET, HEAD |
/files/documents/{versionId}/content/{fileName} |
Get specific version content with filename hint | Jump |
HEAD |
/files/documents/{documentId}/versions/latest/content |
Check whether converted content is ready | Jump |
GET |
/files/documents/{documentId}/versions/{versionId}/conversion/{mediaType}/{subType} |
Poll conversion progress for requested format | Jump |
On all routes, the desired MIME type can be specified either via the Accept request header or the mimetype query string parameter (useful in browser contexts that cannot set headers). If both are provided, the query string takes precedence.
Getting the raw content of a document
To retrieve the original binary content, issue a GET to the content link with no Accept header (or Accept: */*).
Request headers
| Header | Description |
|---|---|
Accept |
MIME type to download. Omit (or use */*) for the original format. Set to a specific MIME type to request a converted version. |
X-Download-Reason |
Reason for the download. See x-download-reason. |
Range |
Request a byte range. E.g. bytes=0-1023. See Partial content. |
If-None-Match |
Returns 304 if the ETag matches (client cache is fresh). |
If-Modified-Since |
Returns 304 if content has not been modified since the given date. |
If-Match |
Returns 412 if the ETag does not match. |
If-Unmodified-Since |
Returns 412 if content has been modified since the given date. |
If-Range |
Combined with Range: if the ETag is stale, returns full content instead of partial. |
Response headers
| Header | Present on | Description |
|---|---|---|
Content-Type |
200, 206 | MIME type of the downloaded content. |
Content-Length |
200, 206 | Total bytes in the response body (or in the partial range). |
Content-Disposition |
200, 206 | Filename. E.g. attachment; filename="report.pdf". |
Supported-Accept |
200, 202, 406, 500 | Comma-separated list of MIME types available for this document. |
Vary |
All | Accept — signals that the response varies by Accept header. |
ETag |
200, 206, 304 | Entity tag for cache validation. |
Last-Modified |
200, 206, 304 | Date the content was last updated (RFC 1123). |
Cache-Control |
200, 206, 304 | no-cache, no-store |
Expires |
200, 206, 304 | Thu, 01 Jan 1970 00:00:00 GMT |
Accept-Ranges |
200, 206 | bytes — indicates byte-range requests are supported. |
Content-Range |
206, 416 | Byte range returned, or total size on 416. E.g. bytes 0-1023/4096 or bytes */4096. |
Location |
202 | URI of the conversion progress endpoint to poll. |
Response codes
| Code | Description |
|---|---|
| 200 OK | Full content returned. |
| 202 Accepted | Conversion required; poll the Location URI for progress. |
| 206 Partial Content | Partial content returned (byte range request). |
| 304 Not Modified | Content unchanged since last request; no body returned. |
| 400 Bad Request | Invalid X-Download-Reason value or malformed mimetype parameter. |
| 404 Not Found | Document or version not found. |
| 406 Not Acceptable | Conversion to the requested MIME type is not supported for this document. |
| 412 Precondition Failed | Conditional request failed (If-Match/If-Unmodified-Since). |
| 416 Range Not Satisfiable | Requested range is outside the content bounds. |
| 500 Internal Server Error | A previous conversion attempt failed. |
Example: Basic download (200 OK)
Request
GET /files/documents/456/versions/latest/content HTTP/1.1
Authorization: Bearer frootymcnooty/vonbootycherooty
Response
HTTP/1.1 200 OK
Content-Type: text/plain
Content-Length: 198
Content-Disposition: attachment; filename="filename.txt"
Supported-Accept: text/plain, application/pdf
Vary: Accept
ETag: "a1b2c3d4"
Last-Modified: Tue, 25 Mar 2025 10:00:00 GMT
Cache-Control: no-cache, no-store
Expires: Thu, 01 Jan 1970 00:00:00 GMT
Accept-Ranges: bytes
Hello - this is the content of your text file. You uploaded it to Huddle to keep it safe and
secure, but now it has been used in an API example by a bad developer and so everybody can see it. Oh no!
Example: Partial content (206 Partial Content)
To download a specific byte range, include a Range header.
Notes:
- Multipart ranges (e.g.
Range: bytes=0-100, 200-300) are not supported. Use a single range. - Syntactically invalid
Rangeheaders are silently ignored and the full content is returned. - If the range start is beyond the end of the file, a 416 is returned.
- If the range end exceeds the file size, it is silently clipped to the end of the file.
Request
GET /files/documents/456/versions/latest/content HTTP/1.1
Authorization: Bearer frootymcnooty/vonbootycherooty
Range: bytes=0-1023
Response
HTTP/1.1 206 Partial Content
Content-Type: text/plain
Content-Range: bytes 0-1023/4096
Accept-Ranges: bytes
ETag: "a1b2c3d4"
Last-Modified: Tue, 25 Mar 2025 10:00:00 GMT
Cache-Control: no-cache, no-store
Expires: Thu, 01 Jan 1970 00:00:00 GMT
... first 1024 bytes of content ...
Example: Range not satisfiable (416)
Request
GET /files/documents/456/versions/latest/content HTTP/1.1
Authorization: Bearer frootymcnooty/vonbootycherooty
Range: bytes=9999-10000
Response
HTTP/1.1 416 Range Not Satisfiable
Content-Range: bytes */198
Example: Conditional GET — not modified (304)
If the client already has a cached copy with a matching ETag, the server returns 304 with no body.
Request
GET /files/documents/456/versions/latest/content HTTP/1.1
Authorization: Bearer frootymcnooty/vonbootycherooty
If-None-Match: "a1b2c3d4"
Response
HTTP/1.1 304 Not Modified
ETag: "a1b2c3d4"
Last-Modified: Tue, 25 Mar 2025 10:00:00 GMT
Cache-Control: no-cache, no-store
Expires: Thu, 01 Jan 1970 00:00:00 GMT
Example: Conditional GET — precondition failed (412)
Request
GET /files/documents/456/versions/latest/content HTTP/1.1
Authorization: Bearer frootymcnooty/vonbootycherooty
If-Match: "outdated-etag"
Response
HTTP/1.1 412 Precondition Failed
Getting the content of a document in a different format
Huddle supports automatic conversion of documents into different formats. For example, Word documents (application/msword) can be converted on-demand to PDF (application/pdf).
To request a converted format, issue a GET to the content link with the Accept header set to the desired MIME type. The supported MIME types for a document are listed in the Supported-Accept response header.
Specifying the same MIME type as the original document is equivalent to omitting the Accept header.
In addition to using the Accept header, the MIME type can be specified via the mimetype query string parameter (useful in browser contexts that cannot set headers).
Example: Content already generated (200 OK)
If a converted copy already exists, it is returned immediately.
Sequence diagram
Client Server
| GET content |
| Accept: application/pdf |
| OR |
| GET |
| content?mimetype=application%2Fpdf |
| ------------------------------> |
| |
| <------------------------------ |
| 200 OK |
| Content-Type: application/pdf |
| ...binary content... |
Request (via Accept header)
GET /files/documents/456/versions/latest/content HTTP/1.1
Authorization: Bearer frootymcnooty/vonbootycherooty
Accept: application/pdf
Request (via query string)
GET /files/documents/456/versions/latest/content?mimetype=application%2Fpdf HTTP/1.1
Authorization: Bearer frootymcnooty/vonbootycherooty
Response
HTTP/1.1 200 OK
Content-Type: application/pdf
Content-Length: 198
Content-Disposition: attachment; filename="filename.pdf"
Supported-Accept: application/msword, text/plain, application/pdf
Vary: Accept
ETag: "a1b2c3d4"
Last-Modified: Tue, 25 Mar 2025 10:00:00 GMT
Cache-Control: no-cache, no-store
Expires: Thu, 01 Jan 1970 00:00:00 GMT
Accept-Ranges: bytes
... generated binary PDF content ...
Example: Content not yet generated — polling flow (202 Accepted)
If no converted copy exists, the server responds with 202 Accepted and a Location header pointing to a progress endpoint. The client should poll this endpoint until the conversion is complete, then follow the content link.
Sequence diagram
Client Server
| GET content |
| Accept: application/pdf |
| OR |
| GET |
| content?mimetype=application%2Fpdf |
| ------------------------------> |
| |
| <------------------------------ |
| 202 Accepted |
| Location: .../conversion/... |
| |
| |
| GET .../conversion/... |
| ------------------------------> |
| |
| <------------------------------ |
| 200 OK |
| (conversion in progress) |
| |
| |
| GET .../conversion/... |
| ------------------------------> |
| |
| <------------------------------ |
| 200 OK |
| Link: .../content/... |
| (conversion complete) |
| |
| |
| GET content |
| Accept: application/pdf |
| ------------------------------> |
| |
| <------------------------------ |
| 200 OK |
| Content-Type: application/pdf |
| ...binary content... |
Request (via Accept header)
GET /files/documents/456/versions/latest/content HTTP/1.1
Authorization: Bearer frootymcnooty/vonbootycherooty
Accept: application/pdf
Request (via query string)
GET /files/documents/456/versions/latest/content?mimetype=application%2Fpdf HTTP/1.1
Authorization: Bearer frootymcnooty/vonbootycherooty
Response (202 Accepted — conversion queued)
HTTP/1.1 202 Accepted
Location: /files/documents/456/versions/235253235/conversion/application/pdf
Supported-Accept: application/msword, text/plain, application/pdf
Vary: Accept
Now poll the Location URI. While conversion is in progress:
Request
GET /files/documents/456/versions/235253235/conversion/application/pdf HTTP/1.1
Authorization: Bearer frootymcnooty/vonbootycherooty
Accept: application/json
Response (still in progress)
HTTP/1.1 200 OK
Content-Type: application/json
{
"status": "InProgress",
"links": [
{"rel": "self", "href": "/files/documents/456/versions/235253235/conversion/application/pdf"},
{"rel": "content", "href": "/files/documents/versions/235253235/content?mimetype=application%2Fpdf"}
]
}
On the next poll, the conversion has completed:
Response (complete)
HTTP/1.1 200 OK
Content-Type: application/json
{
"status": "Complete",
"links": [
{"rel": "self", "href": "/files/documents/456/versions/235253235/conversion/application/pdf"},
{"rel": "content", "href": "/files/documents/versions/235253235/content?mimetype=application%2Fpdf"}
]
}
Follow the content link with the desired Accept header to download the converted file.
Request
GET /files/documents/versions/235253235/content?mimetype=application%2Fpdf HTTP/1.1
Authorization: Bearer frootymcnooty/vonbootycherooty
Accept: application/pdf
Response
HTTP/1.1 200 OK
Content-Type: application/pdf
Content-Length: 198
Content-Disposition: attachment; filename="filename.pdf"
Supported-Accept: application/msword, text/plain, application/pdf
Vary: Accept
ETag: "a1b2c3d4"
Last-Modified: Tue, 25 Mar 2025 10:00:00 GMT
Cache-Control: no-cache, no-store
Expires: Thu, 01 Jan 1970 00:00:00 GMT
Accept-Ranges: bytes
... generated binary PDF content ...
Example: MIME type not supported (406 Not Acceptable)
If the requested MIME type is not listed in Supported-Accept, the server returns 406.
Request
GET /files/documents/456/versions/latest/content HTTP/1.1
Authorization: Bearer frootymcnooty/vonbootycherooty
Accept: video/mp4
Response
HTTP/1.1 406 Not Acceptable
Supported-Accept: application/msword, text/plain, application/pdf
Vary: Accept
Example: Document conversion process failed (500 Internal Server Error)
If a previous conversion attempt failed, the server returns 500.
Request
GET /files/documents/456/versions/latest/content HTTP/1.1
Authorization: Bearer frootymcnooty/vonbootycherooty
Accept: application/pdf
Response
HTTP/1.1 500 Internal Server Error
Supported-Accept: application/msword, text/plain, application/pdf
Vary: Accept
Retrying the conversion for this document may result in success.
Checking if content for a specific MIME type is ready (HEAD)
Use a HEAD request to determine whether converted content is available without downloading it. The response has the same headers as a GET but no body.
Example: Supported MIME types (HEAD, no Accept)
Request
HEAD /files/documents/456/versions/latest/content HTTP/1.1
Authorization: Bearer frootymcnooty/vonbootycherooty
Response
HTTP/1.1 200 OK
Content-Type: application/msword
Content-Length: 198
Content-Disposition: attachment; filename="filename.docx"
Supported-Accept: application/msword, text/plain, application/pdf
Vary: Accept
Example: Content available (HEAD + Accept)
Request (via query string)
HEAD /files/documents/456/versions/latest/content?mimetype=application%2Fmsword HTTP/1.1
Authorization: Bearer frootymcnooty/vonbootycherooty
Request (via Accept header)
HEAD /files/documents/456/versions/latest/content HTTP/1.1
Authorization: Bearer frootymcnooty/vonbootycherooty
Accept: application/msword
Response
HTTP/1.1 200 OK
Content-Type: application/msword
Content-Length: 198
Content-Disposition: attachment; filename="filename.docx"
Supported-Accept: application/msword, text/plain, application/pdf
Vary: Accept
Example: Content requires conversion (HEAD + Accept)
Request
HEAD /files/documents/456/versions/latest/content HTTP/1.1
Authorization: Bearer frootymcnooty/vonbootycherooty
Accept: application/pdf
Response
HTTP/1.1 202 Accepted
Supported-Accept: application/msword, text/plain, application/pdf
Vary: Accept
x-download-reason request header
The X-Download-Reason request header allows the client to record why a file is being downloaded. This value is stored in the audit log's download activity entry.
If X-Download-Reason is not provided, the reason defaults to unspecified.
If an unsupported value is provided, the server returns 400 Bad Request.
The following values are supported:
| Reason code | Description |
|---|---|
unspecified |
Reason not known (default if header is omitted) |
file-preview |
Download to preview the file |
open-file-other-app |
Download to open in another app |
offline-file |
Automatic download for offline files |
offline-file-preview |
Automatic download for offline files (preview) |
suggested-file-preview |
Automatic download for suggested files |
other/{freetext reason} |
Other reason (freetext, max 100 characters) |
Note: For other/ reasons, the freetext portion is HTML-encoded server-side before storage.
Conversion progress endpoint
When the server responds with 202 Accepted to a content request, the Location header contains the URI of a conversion progress endpoint.
Route: GET /files/documents/{documentId}/versions/{versionId}/conversion/{mediaType}/{subType}
An optional ?q={quality} parameter is appended when a quality preference was included in the original request.
Response codes
| Code | status value | Description |
|---|---|---|
| 200 OK | InProgress |
Conversion is still in progress. Continue polling. |
| 200 OK | Complete |
Conversion is complete. Follow the content link to download. |
| 404 Not Found | NotFound |
Conversion record not found (e.g. expired or never started). |
| 500 Internal Server Error | Failed |
Conversion failed. Retrying may succeed. |
Response body
{
"status": "InProgress",
"links": [
{"rel": "self", "href": "/files/documents/456/versions/235253235/conversion/application/pdf"},
{"rel": "content", "href": "/files/documents/versions/235253235/content?mimetype=application%2Fpdf"}
]
}
The self link points back to this progress endpoint. The content link points to the version content endpoint pre-configured with the requested MIME type.