Cloud API
Send photos while you are away, connect a hosted app, or publish artwork from a remote service. Your integration talks to Inkanva’s cloud.
Read the Cloud API guideGuides & API Reference
Bring family photos, daily briefings, and your own automations to Inkanva. Connect through the cloud or control your frame on your local Wi-Fi.
Start here
Send photos while you are away, connect a hosted app, or publish artwork from a remote service. Your integration talks to Inkanva’s cloud.
Read the Cloud API guideConnect a home server, NAS, or automation on your own Wi-Fi. Photos go straight to the frame, with no cloud authentication needed.
Set up Local API| What changes? | Cloud API | Local API |
|---|---|---|
| Frame mode | Managed through the Inkanva App | Local mode, managed in the frame’s browser interface |
| Where requests go | https://api.inkanva.com/openapi | http://<frame-ip>/api/v1 |
| Connection | HTTPS over the internet | HTTP on the same trusted local network, while the frame is awake |
| Access key | Device API key from Frame settings | Separate 32-character access token from local Settings → Local API |
| Sending an image | Base64 in JSON; the new image becomes active | JPEG multipart, or Y4 for 28.5-inch frames; optionally save without displaying |
| Selecting an image | Cloud image ID or name | Filename returned by the frame, such as local_3.jpg |
| Limits | 20 requests/minute per key; 50 MB request payload | 16 MiB upload limit; requests must fit an awake window |
Choose the API for the frame’s current mode. Local mode uses its own photo library and controls; the Inkanva App does not manage the frame in this mode. Cloud keys, image IDs, and JSON upload examples do not apply to Local API.
Cloud API / 01
Use this API to query device information, upload and manage images, and switch the displayed image on an Inkanva Frame remotely. Choose Cloud API for an app, a hosted service, or updates sent from outside the home. Use it with a frame managed through the Inkanva App; for local mode, select the Local API tab.
https://api.inkanva.com/openapiCloud API / 02
Send a new holiday photo to a family frame from your own app or service. Upload the image, then check device sync status.
Monitor heartbeat, battery, charging state, screen geometry, and sync status.
List existing images, remove outdated assets, and select the active image.
Have your backend turn a daily menu, calendar, or studio schedule into an image and publish it to the frame.
Turn generated artwork, task results, briefings, and context-aware updates into visible output.
Cloud API / 03
OpenClaw, Claude Code, ChatGPT, or any tool-calling agent can control Inkanva through a small server-side integration. Keep the device key in the agent runtime, map the API operations to tools, then let the agent inspect, select, upload, and display artwork.
Set up once
Get the keyCopy it from the Frame settings page.
Keep it privateStore it in the agent’s server-side secrets.
Register toolsMap the API operations to callable agent tools.
BASE_URLhttps://api.inkanva.com/openapi
API_KEY<DEVICE_API_KEY>
Every request
User “Display a quiet blue landscape in the studio.”
The agent checks the frame, reuses or creates artwork, displays it, then confirms the active image.
get_frame_statusPOST /deviceCheck online, battery, sync, and current image.
list_frame_imagesPOST /listImagesFind existing artwork before uploading a duplicate.
upload_frame_imagePOST /uploadUpload raw base64 artwork and make it active.
display_frame_imagePOST /displayImageByIdSwitch to an existing image by ID.
delete_frame_imagePOST /deleteImageRemove an image only after user confirmation.
You control an Inkanva Frame with the provided tools.
1. Inspect the frame before changing it when its state is unknown.
2. Reuse a suitable image from the library when possible.
3. For new artwork: generate image → encode raw base64 → upload.
4. Remember that upload makes the new image active immediately.
5. Ask for confirmation before deleting any image.
6. After a write action, check device status and report the result.
Cloud API / 04
Authorization header.Content-Type: application/json header.Authorization: Bearer <API_KEY>
Content-Type: application/json
Cloud API / 05
Exceeding the limit returns HTTP 429.
{ "message": "Too many requests, please try again later." }
Cloud API / 06
/device
Request body: {} or omitted.
{
"firmware": "B100V050610",
"nickname": "Inkanva",
"lastHeartbeat": "2026-06-16T06:56:25.782Z",
"isCharging": false,
"batteryLevel": 100,
"sdUsedSize": 0,
"switchType": "queue",
"switchMinute": 60,
"imageId": 423,
"coverId": null,
"timezone": "America/Adak",
"point": 30,
"screenWidth": 800,
"screenHeight": 480,
"screenRotate": 270,
"synced": false,
"online": false
}
curl -X POST https://api.inkanva.com/openapi/device \
-H "Authorization: Bearer <API_KEY>" \
-H "Content-Type: application/json" \
-d '{}'/upload
{
"base64": "/9j/4AAQSkZJRgABAQEAAAAAAAD...",
"name": "photo.jpg"
}base64 is required and must contain raw base64 image data without a data URL prefix.name is optional and defaults to the original filename.{ "id": 789, "counter": 10 }curl -X POST https://api.inkanva.com/openapi/upload \
-H "Authorization: Bearer <API_KEY>" \
-H "Content-Type: application/json" \
-d '{"base64":"<BASE64_DATA>","name":"photo.jpg"}'/listImages
Request body: {} or omitted.
{
"total": 2,
"list": [
{
"id": 789,
"name": "photo.jpg",
"fileId": "abc123.jpg",
"size": 182736,
"type": "image/jpeg",
"width": 800,
"height": 480,
"createdAt": "2026-05-14T08:00:00.000Z",
"updatedAt": "2026-05-14T08:00:00.000Z",
"current": true
}
]
}total is the number of images belonging to the device.list contains images ordered by ID descending.current is true when the image is currently active.curl -X POST https://api.inkanva.com/openapi/listImages \
-H "Authorization: Bearer <API_KEY>" \
-H "Content-Type: application/json" \
-d '{}'/deleteImage
{ "imageId": 123 }imageId is required and must belong to the device.null.{
"success": true,
"id": 123,
"imageId": 122,
"counter": 11
}{ "message": "imageId is empty" } when the ID is missing or invalid.{ "message": "image not found" } when the image does not belong to the device.curl -X POST https://api.inkanva.com/openapi/deleteImage \
-H "Authorization: Bearer <API_KEY>" \
-H "Content-Type: application/json" \
-d '{"imageId":123}'/image/:apikey/:imageId
The API key is passed in the URL path, so the Authorization header is not required.
apikey.image/jpeg.Binary image stream.
{ "message": "image not found" } for an invalid or unrelated image ID.{ "message": "can not find robot <apikey>" } for an unknown device key.curl -L "https://api.inkanva.com/openapi/image/<API_KEY>/123" \
-o image.jpg/displayImageById
{ "imageId": 123 }{ "success": true }Returns 500 with { "message": "image not found" } when the image does not belong to the device.
curl -X POST https://api.inkanva.com/openapi/displayImageById \
-H "Authorization: Bearer <API_KEY>" \
-H "Content-Type: application/json" \
-d '{"imageId":123}'/displayImageByName
{ "imageName": "photo.jpg" }{ "success": true }Returns 500 with { "message": "image not found" } when the image name is not found.
curl -X POST https://api.inkanva.com/openapi/displayImageByName \
-H "Authorization: Bearer <API_KEY>" \
-H "Content-Type: application/json" \
-d '{"imageName":"photo.jpg"}'Cloud API / 07
Unknown method in URL.
{ "message": "can not find method <method_name>" }Rate limit exceeded.
{ "message": "Too many requests, please try again later." }The device cannot be found, or another server-side failure occurred.
{ "message": "<error_message>" }Cloud API / 08
The Cloud API accepts request payloads up to 50 MB. Ensure base64 image uploads fit within this limit. Local API has its own upload formats and limits.
Local API / 01
Local mode lets you upload photos, choose what is on screen, and set up a slideshow in the frame’s own browser interface. Local API is an optional feature within this mode: turn it on to let a computer or home server control the frame over your home Wi-Fi.
http://<frame-ip>/api/v1Just want to upload photos yourself? Use the frame’s browser interface; no code is needed. Enable Local API when you want access over your home Wi-Fi or want to build an automation. These instructions apply to firmware with Settings → Local API.
Local API / 02
Run your integration on a computer, Raspberry Pi, NAS, or home server that can reach the frame. These are workflows you can build with the API; you provide the schedule, content, and automation logic.
Choose photos from a shared folder and upload a batch with show=0. Enable a daily slideshow so the frame rotates through the saved album without a cloud upload.
Have a local automation, such as one you build with Home Assistant or Node-RED, select a saved welcome image. If the frame is asleep, keep the latest requested scene and send it during the next awake window.
Select a saved image ↗Render today’s calendar, weather, or chores as an image on your home server. Upload it when the frame next connects, then check that the screen refresh succeeded. Online data sources may still need internet access.
Publish your first image ↗Prepare opening hours, a menu, or a workshop schedule on a local computer. Save several images in advance and select one by filename, or use the built-in slideshow. Plan around wake windows rather than instant updates.
Set up a slideshow ↗Keep a collection on the frame and rotate it locally. Your router can provide Wi-Fi even when its internet connection is down. For occasional manual uploads, connect to the frame’s hotspot and use its browser interface.
Connect in local mode ↗Let an agent running on your network prepare artwork or a daily briefing, inspect the frame’s size, and upload a compatible image. Confirm the display result before reporting success; image generation may use a separate online service.
Inspect the frame first ↗Local API / 03
INKANVA-XXXXXX network shown on the display. Open the displayed browser address, usually http://192.168.4.1. Stay connected even if your phone reports no internet.http://inkanva-xxxxxx.local, while the frame is awake. Enter the token when prompted. If the name does not resolve, find the frame’s IP in your router’s device list and use it instead; a DHCP reservation keeps that address consistent.Use the address shown by your frame. The hostname uses the hotspot name in lowercase; xxxxxx is the last six characters of its MAC address. The hotspot address is for direct setup and is different from the address on your home network.
The browser remembers the token for that address. Switching from the hostname to an IP address, or using another browser, requires entering it again. Keep the authenticated control page open during your first API experiment: it sends a heartbeat every 5 seconds to keep the frame available. Close it afterward to allow sleep.
Local API / 04
Run these cURL examples in a terminal on the same local network. Replace the example IP and token with your own, and keep the authenticated frame page open while you follow the steps. This walkthrough is for frames whose uploadFormat is jpg; 28.5-inch frames use the Y4 upload workflow.
FRAME='http://192.168.1.50'
TOKEN='YOUR_LOCAL_ACCESS_TOKEN'
curl --connect-timeout 3 --max-time 5 \
-H "Authorization: Bearer $TOKEN" \
"$FRAME/api/v1/info"Read uploadFormat, uploadWidth, and uploadHeight. Crop your image to those dimensions for predictable framing, and save a baseline (non-progressive) JPEG. If the connection times out, wait for an awake window; an HTTP request cannot wake the frame.
curl -H "Authorization: Bearer $TOKEN" \
-F 'image=@photo.jpg;filename=photo.jpg;type=image/jpeg' \
"$FRAME/api/v1/upload?show=0&uploadId=first-photo-001"A successful upload returns HTTP 201, for example {"file":"local_3.jpg"}. With show=0, the photo is saved without changing the screen. Use a new uploadId for each new image; reuse it only when retrying the same upload.
curl -H "Authorization: Bearer $TOKEN" \
--data-urlencode 'name=local_3.jpg' \
"$FRAME/api/v1/show"Replace local_3.jpg with the filename returned by your upload. HTTP 200 with {"current":"local_3.jpg"} means the display request was accepted. The e-paper refresh finishes separately.
curl -H "Authorization: Bearer $TOKEN" "$FRAME/api/v1/info"Check again about every 2 seconds while refreshing is true. Success means refreshing=false, displayStatus="succeeded", and both lastDisplayFile and displayed match your filename. If the status is failed, the upload may be saved but the screen did not update successfully. Stop polling when finished, and close the control page to let the frame sleep.
Local API / 05
The frame sleeps between connections to save power while the e-paper keeps its image. With the defaults, it sleeps for 300 seconds, connects to Wi-Fi, then waits up to 10 idle seconds for a valid local request. Sleep time excludes connecting, uploading, and refreshing, so this is not an exact five-minute schedule.
| Setting | Default / range | What it means |
|---|---|---|
apiEnabled | Off · 0 / 1 | Enables the scheduled service on home Wi-Fi. |
sleepSeconds | 300 · 60–3600 seconds | Time spent sleeping between service windows. |
awakeSeconds | 10 · 10–3600 seconds | Idle waiting time after Wi-Fi connects; authenticated requests renew it. |
GET /info again every few seconds, with a bounded retry period. Space attempts less than awakeSeconds apart to avoid missing a short window. Stop retrying when finished or after your chosen deadline.POST /heartbeat every 5 seconds while working. Upload activity and display completion also extend the idle window. Opening only the unauthenticated home page does not.423 response means busy; control responses include Retry-After: 2. Uploads and queued or running refreshes finish before automatic sleep.A sleeping frame cannot be woken by an ordinary HTTP request. Queue time-sensitive content for its next connection. On smaller frames, charging may replace deep sleep with a Wi-Fi-off idle period; local access still uses periodic service windows.
Local API / 06
All paths below start at http://<frame-ip>/api/v1. On home Wi-Fi, every API request needs Authorization: Bearer <LOCAL_TOKEN>, including image downloads. Direct hotspot requests do not require a token because the hotspot is the setup entry point.
The examples reuse FRAME and TOKEN from Your First Image. Set them to your frame’s local address and access token before running a command.
Send ordinary POST parameters as application/x-www-form-urlencoded (cURL -d or --data-urlencode), not JSON. Uploads use multipart or the binary format below. Local HTTP has no TLS or cross-origin CORS support: use a client on your trusted LAN or the frame’s own browser interface, and keep the service off the public internet.
| Method | Path | Use it to |
|---|---|---|
| GET | /info | Read device details and the display result. |
| GET | /state | Read full browser-compatible state, including image names. |
| GET | /images | List the local album. |
| GET | /image | Download a photo or preview. |
| GET / POST | /config | Read or change Wi-Fi and wake settings. |
| POST | /upload | Upload a JPEG on supported smaller frames. |
| POST | /upload-y4 | Upload Y4 and a JPEG preview on 28.5-inch frames. |
| POST | /upload-y4-bin | Send the same Y4 content in one binary body. |
| POST | /show | Display a saved photo. |
| POST | /next, /previous | Move through the album. |
| POST | /delete | Delete a saved photo and its associated files. |
| POST | /settings | Set slideshow behavior and orientation. |
| POST | /heartbeat, /sleep | Keep the session open or end it. |
/info · /stateBoth return HTTP 200 JSON. /state also includes an images array of filenames. Use /info to check readiness and confirm display results, even if the SD card is unavailable.
ap for hotspot or sta for home Wi-Fi, plus the current address and network name.idle / pending / succeeded / failed; and the latest requested filename in this boot.displayed value still refers to the previous photo.jpg or y4./images · /image{
"images": ["local_1.jpg", "local_2.jpg"],
"current": "local_2.jpg",
"displayed": "local_1.jpg",
"refreshing": true
}curl -H "Authorization: Bearer $TOKEN" \
"$FRAME/api/v1/image?name=local_1.jpg&thumbnail=1" \
-o thumbnail.jpgUse filenames returned by the frame. Omit thumbnail=1 for the saved JPEG; if no thumbnail exists, that JPEG is returned anyway. On 28.5-inch frames the .jpg entry is the preview, with display data stored in the associated .y4 file. Image downloads can be busy during refresh; the album list uses cached names.
/uploadFor uploadFormat="jpg". Send a decodable baseline JPEG, up to 16 MiB, with an optional JPEG thumbnail no larger than 640 × 640 pixels. Crop to the suggested upload dimensions; the firmware can scale valid JPEGs.
curl -H "Authorization: Bearer $TOKEN" \
-F 'image=@photo.jpg;filename=photo.jpg;type=image/jpeg' \
-F 'thumbnail=@thumbnail.jpg;filename=thumbnail.jpg;type=image/jpeg' \
"$FRAME/api/v1/upload?show=0&uploadId=album-photo-001"thumbnail.jpg for the thumbnail filename; keep thumbnail out of the main image filename.show=0 saves only, useful for batch uploads. Omitting it, or using show=1, saves and requests display.201 returns the assigned filename: {"file":"local_3.jpg"}. Keep it for subsequent show or delete requests.jpgBytes and thumbnailBytes declare file sizes for additional integrity checks.uploadId accepts up to 64 letters, numbers, hyphens, or underscores. Only the most recent successful upload is remembered, including across restarts. If its response is lost, retry the same file with the same ID before starting another upload. This is not a permanent duplicate-photo check./upload-y4 · /upload-y4-binThese models need panel-ready Y4 display data plus a JPEG preview; the JPEG upload endpoint cannot replace that data. For manual uploads, use the frame’s browser interface to prepare the image. Custom integrations must produce the Y4 format below.
Prepare the image for the current orientation and physical panel size. For landscape, rotate it clockwise into the physical orientation, then mirror horizontally and pack two pixels per byte: even x in the low 4 bits, odd x in the high 4 bits. Native color codes are black 0, white 7, yellow 2, blue 1, red 4, and green 3. The Y4 length must be exactly panelWidth × panelHeight / 2 bytes; raw RGB bytes will not work.
curl -H "Authorization: Bearer $TOKEN" \
-F 'display=@photo.y4;filename=photo.y4;type=application/octet-stream' \
-F 'thumbnail=@thumbnail.jpg;filename=thumbnail.jpg;type=image/jpeg' \
"$FRAME/api/v1/upload-y4?show=0&uploadId=poster-001"cat photo.y4 thumbnail.jpg > upload.bin
Y4_BYTES=$(wc -c < photo.y4 | tr -d '[:space:]')
THUMB_BYTES=$(wc -c < thumbnail.jpg | tr -d '[:space:]')
curl -H "Authorization: Bearer $TOKEN" \
-H 'Content-Type: application/octet-stream' \
-H "X-Upload-Y4-Bytes: $Y4_BYTES" \
-H "X-Upload-Thumbnail-Bytes: $THUMB_BYTES" \
-H 'X-Upload-Show: 0' \
-H 'X-Upload-Id: poster-002' \
--data-binary @upload.bin \
"$FRAME/api/v1/upload-y4-bin"The binary body has no separator. cURL sets Content-Length from the file; other clients must also send the exact length. The Y4 and thumbnail byte-count headers are required, and thumbnail length must be greater than zero. X-Upload-Show: 0 saves only; other values request display. X-Upload-Id is optional and has the same retry behavior as uploadId. Query parameters cannot replace these headers.
Keep the combined Y4 and thumbnail payload within 16 MiB and the thumbnail within 640 × 640 pixels. Both routes return HTTP 201 with a .jpg album filename, such as {"file":"local_3.jpg"}. Use that returned name to display the image.
/show · /next · /previous · /delete# Display a saved image
curl -H "Authorization: Bearer $TOKEN" \
--data-urlencode 'name=local_3.jpg' "$FRAME/api/v1/show"
# Move through the album (no request body)
curl -H "Authorization: Bearer $TOKEN" -X POST "$FRAME/api/v1/next"
curl -H "Authorization: Bearer $TOKEN" -X POST "$FRAME/api/v1/previous"Show, next, and previous return HTTP 200 with {"current":"local_3.jpg"} when accepted. Next and previous follow the configured sequential or random playback strategy and wrap around the album. An empty album returns 404. Wait for each refresh to finish before sending another change.
curl -H "Authorization: Bearer $TOKEN" \
--data-urlencode 'name=local_1.jpg' "$FRAME/api/v1/delete"Deletion removes the photo, thumbnail, and any associated Y4 data. HTTP 200 returns ok, current, and refreshing. Deleting the selected photo selects the next one; deleting the last photo shows the local upload guide. Confirm the filename before deleting.
/settingsFor example, rotate through an album once per hour in portrait orientation:
curl -H "Authorization: Bearer $TOKEN" \
-d 'slideshow=1&order=sequence&interval=3600&orientation=portrait' \
"$FRAME/api/v1/settings"All four parameters are required on every request:
0 off, 1 on.sequence or random.300 / 3600 / 21600 / 43200 / 86400 / 604800 seconds: 5 minutes, 1 hour, 6 hours, 12 hours, 1 day, or 1 week.portrait or landscape.HTTP 200 includes orientationChanged, refreshing, and updated upload dimensions. Changing orientation schedules a redraw; prepare future uploads to the new dimensions. These settings do not change the Local API sleep interval.
/configGET reads the current configuration. POST updates it; include apiEnabled=0 or 1 every time. Omitted optional values stay unchanged. For example, allow a longer testing window:
curl -H "Authorization: Bearer $TOKEN" \
--data-urlencode 'apiEnabled=1' \
--data-urlencode 'sleepSeconds=300' \
--data-urlencode 'awakeSeconds=60' \
"$FRAME/api/v1/config"Both return HTTP 200 with apiEnabled, wifiSsid, sleepSeconds, awakeSeconds, token, and hostname. The Wi-Fi password is never returned. Keep the response private because it contains your access token.
wifiSsid: up to 32 bytes; must be nonempty when enabling the API. Optional wifiPassword: up to 64 bytes; omit to retain it, or explicitly send an empty value for an open network.sleepSeconds: 60–3600. awakeSeconds: 10–3600. Invalid parameters return 400 without changing the configuration./heartbeat · /sleepcurl -H "Authorization: Bearer $TOKEN" -X POST "$FRAME/api/v1/heartbeat"
# After the display has finished refreshing:
curl -H "Authorization: Bearer $TOKEN" -X POST "$FRAME/api/v1/sleep"Heartbeat returns 204 with no body. Sleep returns 202, for example {"ok":true,"sleepSeconds":300,"timerWake":true}, and closes the service shortly afterward. If a refresh is pending, sleep returns 423; wait and retry. When both API and slideshow are disabled, timerWake=false; restart the frame to reconnect.
Local API / 07
A connection timeout is different from an HTTP error: the frame may be asleep or joining Wi-Fi. Most API errors return JSON such as {"error":"Image not found."}; image download errors can return plain text.
| Response | What to do |
|---|---|
| 400 · Invalid request | Read error. Check form fields, supported image format, file size, and complete transfer. Storage-write validation can also fail with 400. |
| 401 · Unauthorized | Use the local access token and the exact Bearer header. A Cloud API key will not work. Invalid authentication does not extend the awake window. |
| 404 · Not found | Check the route and use a filename returned by /images. Next / previous also return 404 for an empty album. |
| 409 · Local mode off | Enable local mode in the frame’s browser interface first. |
| 423 · Refresh in progress | Wait for display work to finish, then retry. Honor Retry-After: 2 when present; check progress with /info. |
| 500 · Storage operation failed | Check the error message and SD card availability before retrying a delete or other storage operation. |
| 503 · SD card unavailable | Image operations need the SD card. Check sdReady with /info; device information and configuration remain available. |
Check that Local API is enabled, the frame has left hotspot setup, and your client can reach devices on the same LAN. Guest Wi-Fi isolation can prevent this. Wait for the next wake window. If the .local name fails, use the router-assigned IP. If the Wi-Fi credentials are wrong, restart the frame, reconnect to its hotspot, and correct them.
show=0 saves without displaying. Send /show with the returned filename. Otherwise, wait for refreshing=false and check displayStatus, lastDisplayFile, and displayed. A successful HTTP response alone does not confirm a successful screen refresh.
The open control page sends a heartbeat every 5 seconds. Monitoring scripts also extend the window with authenticated requests. Close the page and stop polling when finished. The frame waits for uploads and display work to complete before sleeping.
To recover your token, restart the frame, join its hotspot, and open Settings → Local API. To return to App activation, use the browser’s Reset device option. Reset removes every image and local setting, including saved Wi-Fi and the token, so keep copies of photos you want to retain before resetting.