Custom Field Types
Learn how to send and receive file uploads, references, galleries, and other complex field types when working with channel entries via the Nimbu REST API.
Channel entries have dynamic custom fields defined per channel. Simple types (string, integer, float, boolean, date, email, json) are sent as primitive values. This page documents the wire format for complex field types: files, references, galleries, and selects.
All examples use the create entry and update entry endpoints:
POST /channels/{channel_id}/entries
PUT /channels/{channel_id}/entries/{entry_id}File Fields
File fields accept uploads as base64-encoded data inside a JSON object with __type: "File".
Upload a file
{
"document": {
"__type": "File",
"filename": "report.pdf",
"attachment": "JVBERi0xLjQKJ..."
}
}| Key | Type | Required | Description |
|---|---|---|---|
__type | string | yes | Must be "File" |
filename | string | yes | Name with extension — used to detect content type |
attachment | string | yes | Raw base64-encoded file content (no data: URI prefix) |
Remove a file
{
"document": {
"__type": "File",
"remove": true
}
}Preserve an existing file
When updating an entry, omit the file field entirely to keep the current file. If you send back the
response object as-is (with url but no attachment), the API also preserves the existing file.
Response format
Without X-Nimbu-Client-Version header:
{
"document": {
"filename": "report.pdf",
"url": "https://cdn.nimbu.io/files/report.pdf",
"content_type": "application/pdf"
}
}With X-Nimbu-Client-Version header set:
{
"document": {
"__type": "File",
"filename": "report.pdf",
"url": "https://cdn.nimbu.io/files/report.pdf",
"content_type": "application/pdf",
"size": 204800,
"width": null,
"height": null,
"version": "abc123",
"checksum": "def456"
}
}For image files, width and height are populated with pixel dimensions.
Code examples
# Encode file and send as base64
curl -X PUT "https://api.nimbu.io/channels/blog/entries/ENTRY_ID" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"document": {
"__type": "File",
"filename": "report.pdf",
"attachment": "'"$(base64 -i report.pdf)"'"
}
}'Single Reference Fields
Single reference fields (belongs_to) link an entry to one entry in another channel.
Set by ID
Pass the entry ID as a plain string:
{
"author": "507f1f77bcf86cd799439011"
}Set by reference object
{
"author": {
"__type": "Reference",
"className": "authors",
"id": "507f1f77bcf86cd799439011"
}
}| Key | Type | Required | Description |
|---|---|---|---|
__type | string | yes | Must be "Reference" |
className | string | yes | Channel slug of the referenced channel |
id | string | yes* | Entry ID (*either id or slug required) |
slug | string | yes* | Entry slug (*either id or slug required) |
Set by slug
{
"author": {
"__type": "Reference",
"className": "authors",
"slug": "john-doe"
}
}Clear a reference
{
"author": null
}Response format
Default (pointer):
{
"author": {
"__type": "Reference",
"className": "authors",
"id": "507f1f77bcf86cd799439011"
}
}With ?include_slugs=true:
{
"author": {
"__type": "Reference",
"className": "authors",
"id": "507f1f77bcf86cd799439011",
"slug": "john-doe"
}
}With ?resolve=author:
{
"author": {
"__type": "Object",
"className": "authors",
"id": "507f1f77bcf86cd799439011",
"name": "John Doe",
"slug": "john-doe"
}
}Multi Reference Fields
Multi reference fields (belongs_to_many) link an entry to multiple entries in another channel.
Replace all references
Pass an array of IDs:
{
"tags": ["507f1f77bcf86cd799439011", "507f1f77bcf86cd799439012"]
}Or use reference objects:
{
"tags": {
"objects": [
{ "__type": "Reference", "className": "tags", "id": "507f1f77bcf86cd799439011" },
{ "__type": "Reference", "className": "tags", "id": "507f1f77bcf86cd799439012" }
]
}
}Add references
Append to existing references without removing current ones:
{
"tags": {
"__op": "AddReference",
"objects": [
{ "__type": "Reference", "className": "tags", "id": "507f1f77bcf86cd799439011" }
]
}
}Remove references
Remove specific references while keeping the rest:
{
"tags": {
"__op": "RemoveReference",
"objects": [
{ "__type": "Reference", "className": "tags", "id": "507f1f77bcf86cd799439011" }
]
}
}Batch operations
Combine add and remove in a single request:
{
"tags": {
"__op": "Batch",
"operations": [
{
"__op": "AddReference",
"objects": [
{ "__type": "Reference", "className": "tags", "id": "new-tag-id" }
]
},
{
"__op": "RemoveReference",
"objects": [
{ "__type": "Reference", "className": "tags", "id": "old-tag-id" }
]
}
]
}
}Response format
{
"tags": {
"__type": "Relation",
"className": "tags",
"objects": [
{ "__type": "Reference", "className": "tags", "id": "507f1f77bcf86cd799439011" },
{ "__type": "Reference", "className": "tags", "id": "507f1f77bcf86cd799439012" }
]
}
}Gallery Fields
Gallery fields hold ordered collections of images with optional captions.
Set gallery images
{
"photos": {
"__type": "Gallery",
"images": [
{
"__type": "GalleryImage",
"file": {
"__type": "File",
"filename": "hero.jpg",
"attachment": "/9j/4AAQSkZJRg..."
},
"caption": "Hero image",
"position": 0
},
{
"__type": "GalleryImage",
"file": {
"__type": "File",
"filename": "detail.jpg",
"attachment": "/9j/4AAQSkZJRg..."
},
"caption": "Detail shot",
"position": 1
}
]
}
}Each GalleryImage accepts:
| Key | Type | Required | Description |
|---|---|---|---|
__type | string | yes | Must be "GalleryImage" |
file | File object | for new images | File upload (same format as file fields) |
caption | string | no | Image caption text |
position | integer | no | Sort order (0-based) |
id | string | for existing | ID of an existing gallery image |
Update an existing image
Include the id of the existing image and only the fields you want to change:
{
"photos": {
"__type": "Gallery",
"images": [
{
"__type": "GalleryImage",
"id": "existing-image-id",
"caption": "Updated caption"
}
]
}
}Remove a gallery image
{
"photos": {
"__type": "Gallery",
"images": [
{
"__type": "GalleryImage",
"id": "image-to-delete",
"remove": true
}
]
}
}Response format
{
"photos": [
{
"id": "img-001",
"caption": "Hero image",
"position": 0,
"file": {
"filename": "hero.jpg",
"url": "https://cdn.nimbu.io/galleries/hero.jpg",
"content_type": "image/jpeg"
}
}
]
}With X-Nimbu-Client-Version header, each image file includes size, width, height, version,
and checksum.
Select & Multi-Select Fields
Select (single)
Pass the option ID as a string:
{
"status": "published"
}Multi-Select
Pass an array of option IDs:
{
"categories": ["news", "featured", "technology"]
}Response Metadata
The X-Nimbu-Client-Version request header controls how much metadata the API returns for file and
gallery fields.
Without the header, file fields return a compact object:
{ "filename": "photo.jpg", "url": "https://...", "content_type": "image/jpeg" }With the header set (any value), file fields return full metadata:
{
"__type": "File",
"filename": "photo.jpg",
"url": "https://...",
"content_type": "image/jpeg",
"size": 245760,
"width": 1920,
"height": 1080,
"version": "v1a2b3",
"checksum": "sha256..."
}Resolving References
When fetching entries, use query parameters to control how references are returned:
| Parameter | Example | Effect |
|---|---|---|
include_slugs | ?include_slugs=true | Adds slug to each reference pointer |
resolve | ?resolve=author | Inlines the full referenced object |
resolve | ?resolve=author,tags | Resolves multiple fields (comma-separated) |
Example — fetch an entry with resolved author:
GET /channels/blog/entries/ENTRY_ID?resolve=authorComplete Example
Creating an entry with multiple field types combined:
{
"title": "New Blog Post",
"body": "Article content here...",
"published": true,
"publish_date": "2025-03-15",
"hero_image": {
"__type": "File",
"filename": "hero.jpg",
"attachment": "/9j/4AAQSkZJRg..."
},
"author": {
"__type": "Reference",
"className": "authors",
"slug": "john-doe"
},
"tags": {
"objects": [
{ "__type": "Reference", "className": "tags", "slug": "javascript" },
{ "__type": "Reference", "className": "tags", "slug": "tutorial" }
]
},
"gallery": {
"__type": "Gallery",
"images": [
{
"__type": "GalleryImage",
"file": {
"__type": "File",
"filename": "screenshot-1.png",
"attachment": "iVBORw0KGgo..."
},
"caption": "Step 1",
"position": 0
}
]
},
"status": "published",
"related_topics": ["news", "featured"]
}curl -X POST "https://api.nimbu.io/channels/blog/entries" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d @entry.jsonLocalized Fields (content_locale)
Channels can mark custom fields as localized. On a site with more than one locale, the entry JSON
returns those fields in the site's default locale at the top level and every locale under
translations:
{
"id": "66f0a1b2c3d4e5f60718293a",
"title": "Our story",
"translations": {
"nl": { "title": "Ons verhaal" },
"en": { "title": "Our story" }
}
}Reading
?content_locale=<code> selects the working locale for a request. The locale must be enabled on the
site, otherwise the API answers 422 Invalid content_locale: <code> is not enabled for this site.
On reads it does not change the top-level values — those stay in the default locale — but it
scopes locale-aware operations such as search:
curl "https://api.nimbu.io/channels/blog/entries?search=story&content_locale=nl" \
-H "Authorization: Bearer $TOKEN"Read translated values from the translations map, not from the top-level fields.
Writing
Pass content_locale on the update request to write the localized fields of that locale:
curl -X PUT "https://api.nimbu.io/channels/blog/entries/66f0a1b2c3d4e5f60718293a?content_locale=en" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{ "title": "Our story" }'Pending verification. For pages, a write with
content_locale=enis proven to store only theenvalue and leave the default locale untouched. Channel entries use the same locale handling and processor, but no server test currently asserts that an entry update withcontent_locale=enleaves the default-locale value byte-identical, and one field report suggests it may not. Until that is confirmed here, re-read the entry after a localized write and check the default locale intranslationsbefore relying on it in automation.
Non-localized fields ignore content_locale and are always written as-is. Pages accept a nested
translations object for multi-locale writes in one request; see
Working with Pages.
Common Gotchas
__typeis required on File, GalleryImage, and Gallery objects. For references,__typeis required when using the object format — but single references also accept a plain ID string.- Base64 must be raw — do not include
data:image/png;base64,prefixes. Send only the base64 string. classNameis the channel slug — use the human-readable channel identifier (e.g."authors","blog-posts"), not an internal class name.- Multi-reference semantics — sending an array of IDs replaces all references. Use
AddReference/RemoveReferenceto modify incrementally. - Gallery image ordering — set
positionexplicitly. Images without a position may appear in arbitrary order. - File size limits — base64 encoding increases payload size by ~33%. For very large files, consider using the standalone upload endpoint first, then reference the upload.
- Omit unchanged fields — when updating an entry, only include fields you want to change. Omitted
fields are preserved. Sending a file field without
attachmentpreserves the existing file.
Channel Access Control
How channel-level ACLs and per-entry ACLs work for channel entries in the REST API, themes, and customer-session requests.
Working with Pages
Read and safely change page content through the REST API: the page document, merge vs replace updates, surgical batch operations with ETags, drafts and preview tokens, schema and item reads, and page versions.