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)"'"
}
}'import fs from "fs";
const body = {
document: {
__type: "File",
filename: "report.pdf",
attachment: fs.readFileSync("report.pdf").toString("base64"),
},
};
const res = await fetch(
`https://api.nimbu.io/channels/blog/entries/${entryId}`,
{
method: "PUT",
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json",
},
body: JSON.stringify(body),
}
);require "net/http"
require "json"
require "base64"
body = {
document: {
__type: "File",
filename: "report.pdf",
attachment: Base64.strict_encode64(File.binread("report.pdf"))
}
}
uri = URI("https://api.nimbu.io/channels/blog/entries/#{entry_id}")
req = Net::HTTP::Put.new(uri)
req["Authorization"] = "Bearer #{token}"
req["Content-Type"] = "application/json"
req.body = body.to_json
Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |http| http.request(req) }import requests
import base64
with open("report.pdf", "rb") as f:
encoded = base64.b64encode(f.read()).decode("utf-8")
body = {
"document": {
"__type": "File",
"filename": "report.pdf",
"attachment": encoded,
}
}
requests.put(
f"https://api.nimbu.io/channels/blog/entries/{entry_id}",
headers={"Authorization": f"Bearer {token}"},
json=body,
)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..."
}TIP
Set X-Nimbu-Client-Version to any value (e.g. "1") to get the richest response format. This is recommended for API integrations that need file dimensions or checksums.
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.jsonconst res = await fetch("https://api.nimbu.io/channels/blog/entries", {
method: "POST",
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json",
},
body: JSON.stringify(entryData),
});uri = URI("https://api.nimbu.io/channels/blog/entries")
req = Net::HTTP::Post.new(uri)
req["Authorization"] = "Bearer #{token}"
req["Content-Type"] = "application/json"
req.body = entry_data.to_json
Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |http| http.request(req) }requests.post(
"https://api.nimbu.io/channels/blog/entries",
headers={"Authorization": f"Bearer {token}"},
json=entry_data,
)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.