Files & Content
How to attach, upload, and download binary content on the Arke network.
Overview
In Arke, any entity can have binary content attached. Files, documents, images, and other binary data are stored in R2 and referenced via the entity's properties.content map. This unified model means:
- A
fileentity can have multiple content versions (v1, thumbnail, etc.) - A
folderentity can have an attached cover image - A
userentity can have a profile photo - Any custom entity type can store binary data
Content is stored using content-addressed identifiers (CIDs), ensuring integrity and enabling deduplication.
Content Map Structure
Content is stored in properties.content as a map of keys to content entries:
{
"properties": {
"content": {
"v1": {
"cid": "bafkrei...",
"size": 1048576,
"content_type": "application/pdf",
"filename": "document.pdf",
"uploaded_at": "2025-01-15T10:00:00Z"
},
"thumbnail": {
"cid": "bafkrei...",
"size": 10240,
"content_type": "image/png",
"uploaded_at": "2025-01-15T10:01:00Z"
}
}
}
}Each content entry contains:
| Field | Type | Description |
|---|---|---|
cid | string | Content identifier (computed from bytes) |
size | number | File size in bytes |
content_type | string | MIME type |
filename | string? | Original filename (for download headers) |
uploaded_at | string | ISO 8601 timestamp |
Uploading Content
Direct Upload (Recommended for files < 5MB)
Upload binary content directly through the API:
POST /entities/{id}/content?key=v1&filename=document.pdf
Content-Type: application/pdf
Authorization: Bearer <token>
<binary file data>Query parameters:
key(required) - Content key (e.g., "v1", "original", "thumbnail")filename(optional) - Filename for Content-Disposition header on download
Response:
{
"id": "01KFNR1234567890ABCDEFGH",
"cid": "bafyrei_newversion...",
"content": {
"key": "v1",
"cid": "bafkrei...",
"size": 1048576,
"content_type": "application/pdf",
"filename": "document.pdf",
"uploaded_at": "2025-01-15T10:00:00Z"
},
"prev_cid": "bafyrei_oldversion..."
}The system:
- Streams content directly to R2 storage
- Computes the CID from file bytes
- Updates the entity with the new content entry
- Creates a new entity version
Presigned URL Upload (Recommended for files > 5MB)
For large files, upload directly to R2 using a presigned URL:
Step 1: Compute CID client-side
Hash your file content using BLAKE3 to get a CID. Libraries like multiformats can help.
Step 2: Get presigned URL
POST /entities/{id}/content/upload-url
Content-Type: application/json
Authorization: Bearer <token>
{
"cid": "bafkrei...",
"content_type": "application/pdf",
"size": 52428800
}Response:
{
"upload_url": "https://...",
"expires_at": "2025-01-15T10:15:00Z"
}Step 3: Upload directly to R2
PUT <upload_url>
Content-Type: application/pdf
<binary file data>Step 4: Complete the upload
POST /entities/{id}/content/complete
Content-Type: application/json
Authorization: Bearer <token>
{
"key": "v1",
"cid": "bafkrei...",
"size": 52428800,
"content_type": "application/pdf",
"filename": "large-file.pdf",
"expect_tip": "bafyrei_current..."
}Size Limits
Maximum file size is 500 MB. Files larger than this return HTTP 413.
Downloading Content
GET /entities/{id}/content?key=v1
Authorization: Bearer <token>Query parameters (one optional):
key- Content key to downloadcid- Content CID to download (alternative to key)
If neither is specified, returns the first content key alphabetically.
Response headers:
Content-Type: MIME type of the contentContent-Length: File size in bytesContent-Disposition:attachment; filename="..."(if filename was set)
The response is streamed directly from R2 storage.
Managing Content
Adding Multiple Content Versions
Upload additional content with different keys:
# Upload original
POST /entities/{id}/content?key=original
Content-Type: image/png
<original image>
# Upload thumbnail
POST /entities/{id}/content?key=thumbnail
Content-Type: image/png
<thumbnail image>
# Upload WebP version
POST /entities/{id}/content?key=webp
Content-Type: image/webp
<webp image>Renaming Content Keys
Rename a content key without re-uploading:
PATCH /entities/{id}/content
Content-Type: application/json
Authorization: Bearer <token>
{
"old_key": "v1",
"new_key": "original",
"expect_tip": "bafyrei..."
}Removing Content
Remove a content entry from the entity:
DELETE /entities/{id}/content?key=thumbnail&expect_tip=bafyrei...
Authorization: Bearer <token>The R2 file is preserved for version history—only the metadata reference is removed.
Creating File Entities
While any entity can have content, the file type is optimized for file management:
POST /entities
Content-Type: application/json
Authorization: Bearer <token>
{
"type": "file",
"properties": {
"label": "Moby Dick (PDF)",
"description": "The complete novel"
},
"collection": "01KFNR0H0Q791Y1SMZWEQ09FGV"
}Then upload content:
POST /entities/{id}/content?key=v1&filename=moby-dick.pdf
Content-Type: application/pdf
Authorization: Bearer <token>
<binary file data>Uploading New Versions
To upload a new version of content, simply upload with a new key:
# Original upload
POST /entities/{id}/content?key=v1&filename=draft.pdf
# Revised version
POST /entities/{id}/content?key=v2&filename=final.pdfPrevious versions remain accessible via entity version history. You can also overwrite an existing key if you want to replace content in place.
Permission Actions
Content operations use standard entity permissions:
| Action | Description |
|---|---|
entity:view | Download content |
entity:update | Upload, rename, or remove content |
Best Practices
-
Use meaningful keys -
original,thumbnail,v1,processedare clearer thanfile1,file2 -
Store metadata in properties - Put searchable metadata (title, author, etc.) in entity properties, not just in the filename
-
Use presigned URLs for large files - Avoids streaming through the API worker
-
Leverage multiple content slots - Store derivatives (thumbnails, transcoded versions) on the same entity
-
Set filenames for downloads - Include the
filenameparameter so browsers save with the correct name