5. Blobs and references¶
JMAP separates structured data (rows in your tables) from binary blobs
(images, attachments, …). Blobs get their own HTTP endpoints and a dedicated
capability — urn:ietf:params:jmap:blob.
Upload¶
Two paths:
Out-of-band HTTP upload — best for large payloads:
curl -X POST "http://localhost:8000/jmap/upload/$ACCOUNT_ID" \
-H "Authorization: Bearer $TOKEN" \
-H 'Content-Type: image/png' \
--data-binary @logo.png
Returns {"accountId": "...", "blobId": "<id>", "type": "image/png", "size": 42}.
Inline upload via JMAP — best when you can batch with other calls:
["Blob/upload", {
"accountId": "ACC",
"create": {
"k1": {
"data": [{"data:asText": "hello"}],
"type": "text/plain"
}
}
}, "c0"]
Download¶
curl "http://localhost:8000/jmap/download/$ACCOUNT_ID/<blob-id>/logo.png" \
-H "Authorization: Bearer $TOKEN" -o logo.png
The filename in the path is a hint for the Content-Disposition header — the
server uses it verbatim.
Reference blobs from your objects¶
Many capabilities want to attach blobs to their data type. E.g. an email attachment is a blob referenced from a Mailbox; a Todo could have a logo blob.
Two pieces:
- Store the blob id on your object (just a string column).
- Write a
BlobReferencerow soBlob/lookupcan answer "where is this blob used?":
from jmaple.db.models import BlobReference
ctx.db.add(BlobReference(
blob_id=todo.logo_blob_id,
account_id=account_id,
data_type="Todo",
object_id=todo.id,
))
Blob/lookup walks BlobReference to enumerate objects pointing at a blob —
useful for orphan-cleanup and "what depends on this attachment".
Result references — chain calls¶
A common pattern: create a blob via Blob/upload, then save its id on a Todo
in the same request. Result references let you pipe one method's response into
the next without an extra round-trip:
{
"using": ["urn:ietf:params:jmap:core", "urn:ietf:params:jmap:blob", "urn:example:todos"],
"methodCalls": [
["Blob/upload", {
"accountId": "ACC",
"create": {"k1": {"data": [{"data:asText": "hi"}], "type": "text/plain"}}
}, "c0"],
["Todo/set", {
"accountId": "ACC",
"create": {
"t1": {
"text": "with attached blob",
"#logoBlobId": {"resultOf": "c0", "name": "Blob/upload", "path": "/created/k1/blobId"}
}
}
}, "c1"]
]
}
The #logoBlobId key (note the leading #) marks a result reference. The
framework evaluates the JSON Pointer against the response of call c0 and
substitutes the resolved value before validating Todo/set args.
Next: push notifications →