Widget Templates
Persistent widgets live on the Home Screen, the Lock Screen, StandBy, and the
Apple Watch. Unlike Live Activities, they are not tied to an ongoing activity:
you create a widget once in the app, picking its template and slug, then
update its content whenever you like via the
API, and Aivi pushes the change to every
device that displays it.
Choosing a template
Section titled “Choosing a template”Thirteen templates cover the common shapes of glanceable data, and two of them take presses. Pick the one that matches yours; each card jumps to the template’s schema and examples.
The content object you send
must match the widget’s template schema. Widget
templates reuse the Live Activity building blocks (
colors, icons,
values, and
formatters), so the vocabulary is the same across
both.
Every template renders at every Home Screen size, small through the near-fullscreen extra-large portrait size on iOS 27, on the Lock Screen, and on the Apple Watch. The data-rich templates put the larger canvases to work with more data; the simpler ones scale to fit.
Freshness
Section titled “Freshness”Widgets are Aivi’s persistent, best-effort layer. Knowing how updates travel helps you pick the right tool:
- Content pushes arrive in minutes, not seconds. Updating a widget stores the content and sends a low-priority background push to every device showing it. iOS batches and budgets these pushes to save battery, so a change typically appears within minutes: often faster, occasionally slower (Low Power Mode, a sleeping device). Design for “latest known state”, not for real-time.
- Predictable data stays live without pushes. Anything the device can
compute on its own keeps ticking between pushes: the
Progress template’s
end_datecountdown and self-advancing bar, the Countdown target, thetime_since/time_untilformatters, and the Prices timeline highlighting the current period. One push can keep a widget correct for hours or days; prefer this over frequent pushes. - Real-time belongs to Live Activities. If you need second-by-second updates during an ongoing task, use a Live Activity: that is the layer iOS budgets for real-time. A widget can sit alongside it as the persistent summary.
Marking content as stale
Section titled “Marking content as stale”By default a widget shows its last content indefinitely, with no judgement
about age; only you know your update cadence. Set stale_after (in seconds)
to opt in to staleness: once the content is older than the threshold, the
widget visually demotes it and shows an “as of 14:32” line instead of
presenting old data as current. You can set it when creating the widget in
the app, or per request via the API.
Status Template
Section titled “Status Template”ID: status
At-a-glance state: an icon, a hero reading, and a state line.
Home Assistant
Section titled “Home Assistant”The Aivi integration sets up a Status widget from a form in Home Assistant, with a live preview. To use a plain automation instead, import the blueprint:
API schema
Section titled “API schema”| Field | Type | Description | Required? |
|---|---|---|---|
template | "status" | Identifies this template. | Yes |
value | Value | The hero reading. Supports text colors and the self-ticking time_since / time_until formatters. When absent, the state takes its place. | No |
state | Value | The supporting line under the hero. | No |
icon | string | Icon | An SF Symbols icon name or an Icon with rendering options. | No |
accent | Color | Palette color that themes the whole widget. Falls back to the icon color. | No |
tap_url | string | Tap action URL to open when the widget is tapped. | No |
Example payload
Section titled “Example payload”{ "template": "status", "value": { "value": "21°" }, "state": { "value": "Comfortable", "text_color": "green" }, "icon": { "name": "thermometer.medium", "primary_color": "orange" }}Progress Template
Section titled “Progress Template”ID: progress
A live progress bar or countdown. With an end_date set, a single push keeps
the widget counting down and the bar advancing on its own.
Home Assistant
Section titled “Home Assistant”The Aivi integration sets up a Progress widget from a form in Home Assistant, with a live preview. To use a plain automation instead, import the blueprint:
API schema
Section titled “API schema”| Field | Type | Description | Required? |
|---|---|---|---|
template | "progress" | Identifies this template. | Yes |
progress | ProgressBar | Progress bar configuration: a simple single-color bar or a segmented bar. | Yes |
state | Value | The state line (for example “Washing”). | No |
end_date | string | ISO 8601 timestamp. When set in the future, the widget shows a live countdown and, for simple bars, a self-advancing bar, with no extra pushes needed. Segmented bars stay static. | No |
icon | string | Icon | An SF Symbols icon name or an Icon with rendering options. | No |
accent | Color | Palette color that themes the whole widget. Falls back to the icon color. | No |
tap_url | string | Tap action URL to open when the widget is tapped. | No |
Example payload
Section titled “Example payload”{ "template": "progress", "progress": { "style": "simple", "value": 0.65, "color": "cyan" }, "state": { "value": "Washing" }, "end_date": "2026-07-01T15:42:00Z", "icon": { "name": "washer.fill", "primary_color": "cyan" }}Gauge Template
Section titled “Gauge Template”ID: gauge
A numeric reading on an instrument dial.
Home Assistant
Section titled “Home Assistant”The Aivi integration sets up a Gauge widget from a form in Home Assistant, with a live preview. To use a plain automation instead, import the blueprint:
API schema
Section titled “API schema”| Field | Type | Description | Required? |
|---|---|---|---|
template | "gauge" | Identifies this template. | Yes |
value | float | The reading. | Yes |
min_value | float | Dial minimum. Default: 0. | No |
max_value | float | Dial maximum. Default: 1. | No |
unit | string | Unit shown with the reading (for example "°C"). | No |
label | string | Label shown with the reading (for example "Living room"). | No |
color | Color | Solid dial color. | No |
gradient | Color[] | 2–4 gradient stops for the dial. | No |
icon | string | Icon | An SF Symbols icon name or an Icon with rendering options. | No |
accent | Color | Palette color that themes the whole widget. Falls back to the icon color. | No |
tap_url | string | Tap action URL to open when the widget is tapped. | No |
Example payload
Section titled “Example payload”{ "template": "gauge", "value": 21.5, "min_value": 0, "max_value": 40, "unit": "°C", "label": "Living room", "icon": { "name": "thermometer.medium" }}Battery Template
Section titled “Battery Template”ID: battery
Device battery levels in Apple’s Batteries idiom: level rings with device icons, and a device list at the large sizes. Made for home-automation devices (vacuums, locks, cameras, sensors) that Apple’s own widget cannot show.
Home Assistant
Section titled “Home Assistant”The Aivi integration sets up a Battery widget from a form in Home Assistant, with a live preview. To use a plain automation instead, import the blueprint (Home Assistant 2025.7 or newer):
API schema
Section titled “API schema”| Field | Type | Description | Required? |
|---|---|---|---|
template | "battery" | Identifies this template. | Yes |
devices | Device[] | 1–8 devices, shown in the order you send them. Small and medium show up to four rings; large lists up to eight devices. | Yes |
icon | string | Icon | An SF Symbols icon name or an Icon with rendering options. | No |
accent | Color | Palette color that themes the whole widget. Falls back to the icon color. | No |
tap_url | string | Tap action URL to open when the widget is tapped. | No |
Device object
Section titled “Device object”| Field | Type | Description | Required? |
|---|---|---|---|
name | string | Device name, shown in the large list. | Yes |
level | float | Battery percentage, 0–100. | Yes |
charging | bool | Adds the charging bolt. Default: false. | No |
icon | string | Icon | Symbol shown inside the ring. | No |
color | Color | Ring color override. By default rings are green, or red at 20 percent and below. | No |
Example payload
Section titled “Example payload”{ "template": "battery", "devices": [ { "name": "Robot vacuum", "level": 68, "icon": "fan.fill" }, { "name": "Door lock", "level": 45, "charging": true }, { "name": "Doorbell cam", "level": 92, "icon": "video.fill" } ]}Budget Template
Section titled “Budget Template”ID: budget
A top-level budget plus up to six category breakdowns, made for finance and budgeting. Renders at the large widget sizes too.
Amounts are structured money, not preformatted strings: send numbers or decimal strings, and the device formats them for the user’s locale and the given currency (symbol placement, grouping, and fraction digits, so JPY gets 0 and USD gets 2). Prefer decimal strings: a JSON number cannot carry a trailing zero.
Home Assistant
Section titled “Home Assistant”The Aivi integration sets up a Budget widget from a form in Home Assistant, with a live preview. To use a plain automation instead, import the blueprint (Home Assistant 2025.7 or newer):
API schema
Section titled “API schema”| Field | Type | Description | Required? |
|---|---|---|---|
template | "budget" | Identifies this template. | Yes |
total | Entry | The top-level budget line. The widget name serves as its label. | Yes |
categories | Entry[] | Up to 6 category lines, shown in the order you send them. | No |
currency | string | ISO 4217 code (3 uppercase letters); one currency per widget. | Yes |
display | "remaining" | "spent" | Which figure headlines everywhere; the ring follows it, so remaining mode drains toward empty while spent mode fills up. Overspending shows a negative remainder in red. Default: "remaining". | No |
subtitle | Value | Shown with the total, for example “20 days left”. Supports the time_until formatter for a self-updating line. | No |
icon | string | Icon | An SF Symbols icon name or an Icon with rendering options. | No |
accent | Color | Palette color that themes the whole widget. | No |
tap_url | string | Tap action URL to open when the widget is tapped. | No |
Budget entry
Section titled “Budget entry”| Field | Type | Description | Required? |
|---|---|---|---|
spent | number | string | Amount spent. | Yes |
budget | number | string | Budgeted amount, greater than 0. When present the entry gets a progress ring and the total shows an “of $1,500” line. When absent the entry shows the spent amount with a plain icon. | No |
label | string | The category name. | No |
icon | string | Icon | An SF Symbols icon name or an Icon with rendering options. | No |
color | Color | The ring color. Falls back to the icon color, then the accent. | No |
Example payload
Section titled “Example payload”{ "template": "budget", "currency": "USD", "display": "remaining", "total": { "spent": "519.30", "budget": "1500", "icon": { "name": "banknote", "primary_color": "yellow" }, "color": "yellow" }, "subtitle": { "value": "20 days left" }, "categories": [ { "spent": "157.60", "budget": "300", "label": "Food", "icon": "fork.knife", "color": "blue" } ]}Trend Template
Section titled “Trend Template”ID: trend
A current reading with a compact history sparkline: temperature, humidity, power draw, or anything you already track as a series. You post the recent history; the device draws and scales the chart. Renders at the large widget sizes too.
Home Assistant
Section titled “Home Assistant”The Aivi integration sets up a Trend widget from a form in Home Assistant, with a live preview. To use a plain automation instead, import the blueprint:
API schema
Section titled “API schema”| Field | Type | Description | Required? |
|---|---|---|---|
template | "trend" | Identifies this template. | Yes |
value | float | The current reading, shown as the hero. | Yes |
points | float[] | Recent history, 2–48 points, oldest first. The device scales the sparkline to the data. | Yes |
unit | string | Unit shown with the reading (for example "°"). | No |
min_value | float | Fixed lower chart bound. Unset scales to the data. | No |
max_value | float | Fixed upper chart bound. Unset scales to the data. | No |
state | Value | Supporting line under the hero (for example “Rising”). | No |
color | Color | Sparkline color. Falls back to the accent, then the icon color. | No |
icon | string | Icon | An SF Symbols icon name or an Icon with rendering options. | No |
accent | Color | Palette color that themes the whole widget. Falls back to the icon color. | No |
tap_url | string | Tap action URL to open when the widget is tapped. | No |
Example payload
Section titled “Example payload”{ "template": "trend", "value": 21.4, "unit": "°", "points": [18.2, 18.9, 19.6, 20.8, 21.6, 21.2, 20.9, 21.4], "state": { "value": "Rising" }, "icon": { "name": "thermometer.medium", "primary_color": "orange" }}Countdown Template
Section titled “Countdown Template”ID: countdown
A moment in time rendered as a self-updating countdown: garbage day, the next departure, an appliance finishing. One push sets the target; the device keeps the countdown current with no further pushes.
Home Assistant
Section titled “Home Assistant”The Aivi integration sets up a Countdown widget from a form in Home Assistant, with a live preview. To use a plain automation instead, import the blueprint:
API schema
Section titled “API schema”| Field | Type | Description | Required? |
|---|---|---|---|
template | "countdown" | Identifies this template. | Yes |
target | string | ISO 8601 timestamp (with timezone) to count down to. | Yes |
subtitle | Value | Supporting line (for example “Paper & cardboard”). | No |
expired_text | string | Shown once the target has passed. Without it, the widget shows the time elapsed since the target instead. | No |
icon | string | Icon | An SF Symbols icon name or an Icon with rendering options. | No |
accent | Color | Palette color that themes the whole widget. Falls back to the icon color. | No |
tap_url | string | Tap action URL to open when the widget is tapped. | No |
Example payload
Section titled “Example payload”{ "template": "countdown", "target": "2026-07-04T06:30:00Z", "subtitle": { "value": "Paper & cardboard" }, "expired_text": "Out now", "icon": { "name": "trash.fill", "primary_color": "green" }}List Template
Section titled “List Template”ID: list
Rows of labeled values: the generic multi-entity readout for anything that does not fit a more specific template. Small and medium show up to four rows (small drops the labels to fit); large lists up to eight. Renders at the large widget sizes too.
Home Assistant
Section titled “Home Assistant”The Aivi integration sets up a List widget from a form in Home Assistant, with a live preview. To use a plain automation instead, import the blueprint (Home Assistant 2025.7 or newer):
API schema
Section titled “API schema”| Field | Type | Description | Required? |
|---|---|---|---|
template | "list" | Identifies this template. | Yes |
rows | Row[] | 1–8 rows, shown in the order you send them. | Yes |
icon | string | Icon | An SF Symbols icon name or an Icon with rendering options. | No |
accent | Color | Palette color that themes the whole widget. Falls back to the icon color. | No |
tap_url | string | Tap action URL to open when the widget is tapped. | No |
Row object
Section titled “Row object”| Field | Type | Description | Required? |
|---|---|---|---|
label | string | The row label. | Yes |
value | Value | Trailing value, with optional text color and formatters. | No |
icon | string | Icon | Leading symbol. | No |
Example payload
Section titled “Example payload”{ "template": "list", "rows": [ { "label": "Living room", "value": { "value": "21.4°" }, "icon": { "name": "thermometer.medium", "primary_color": "orange" } }, { "label": "Front door", "value": { "value": "Locked", "text_color": "green" }, "icon": "lock.fill" }, { "label": "Humidity", "value": { "value": "54%" }, "icon": { "name": "humidity.fill", "primary_color": "teal" } } ]}Prices Template
Section titled “Prices Template”ID: prices
A price timeline for dynamic tariffs: hourly electricity prices and the like. Post the periods you know (for example today plus tomorrow, hourly); the device charts them and highlights the current one, so a single nightly push stays correct all day. Renders at the large widget sizes too.
Home Assistant
Section titled “Home Assistant”The Aivi integration sets up a Prices widget from a form in Home Assistant, with a live preview. To use a plain automation instead, import the blueprint:
API schema
Section titled “API schema”| Field | Type | Description | Required? |
|---|---|---|---|
template | "prices" | Identifies this template. | Yes |
entries | Entry[] | 1–48 pricing periods in strictly increasing start order. Each period lasts until the next one begins. | Yes |
unit | string | Display unit (for example "ct/kWh"); prices are unit-agnostic numbers. | No |
icon | string | Icon | An SF Symbols icon name or an Icon with rendering options. | No |
accent | Color | Palette color that themes the whole widget. Falls back to the icon color. | No |
tap_url | string | Tap action URL to open when the widget is tapped. | No |
Price entry
Section titled “Price entry”| Field | Type | Description | Required? |
|---|---|---|---|
start | string | ISO 8601 timestamp the period starts at. | Yes |
price | float | The price for the period. | Yes |
level | "low" | "medium" | "high" | Explicit price banding. Unset lets the device derive bands from the posted range. | No |
Example payload
Section titled “Example payload”{ "template": "prices", "unit": "ct/kWh", "entries": [ { "start": "2026-07-02T00:00:00+02:00", "price": 14.2 }, { "start": "2026-07-02T01:00:00+02:00", "price": 12.8 }, { "start": "2026-07-02T02:00:00+02:00", "price": 11.9, "level": "low" }, { "start": "2026-07-02T03:00:00+02:00", "price": 22.4 } ]}Energy Template
Section titled “Energy Template”ID: energy
Home energy at a glance: generation sources, grid, battery, and home consumption. Sources are generation of any kind (solar, wind, hydro, a generator) and default to the solar presentation when unnamed. Renders at the large widget sizes too.
Home Assistant
Section titled “Home Assistant”The Aivi integration sets up an Energy widget from a form in Home Assistant, with a live preview. To use a plain automation instead, import the blueprint (Home Assistant 2025.7 or newer):
API schema
Section titled “API schema”| Field | Type | Description | Required? |
|---|---|---|---|
template | "energy" | Identifies this template. | Yes |
sources | Node[] | Up to 3 generation sources. | No |
grid | Node | The grid connection. | No |
battery | Node | The home battery. | No |
home | Node | Home consumption. | No |
icon | string | Icon | An SF Symbols icon name or an Icon with rendering options. | No |
accent | Color | Palette color that themes the whole widget. Falls back to the icon color. | No |
tap_url | string | Tap action URL to open when the widget is tapped. | No |
Energy node
Section titled “Energy node”All four slots share this one node shape; the slot a node sits in decides
its defaults and how its power sign is read. In practice level belongs
to the battery node and name chiefly renames generation sources; the
remaining fields apply everywhere.
| Field | Type | Description | Required? |
|---|---|---|---|
name | string | Display label overriding the slot’s default (“Solar”, “Grid”, …); chiefly for generation sources (“Wind”, “Hydro”). | No |
power | float | Instantaneous power in watts. The sign follows the slot’s convention (see above). | Yes |
today | float | Today’s cumulative energy in kWh. | No |
level | float | Charge percent (0–100); battery node only. | No |
icon | string | Icon | Symbol for the node. | No |
color | Color | Color override for the node. | No |
Example payload
Section titled “Example payload”{ "template": "energy", // Unnamed sources render as "Solar"; set "name" for "Wind", "Hydro", … "sources": [{ "power": 3200, "today": 12.4 }], // Negative grid power: exporting. Positive would be importing. "grid": { "power": -1400, "today": 3.1 }, // Positive battery power: charging. Negative would be discharging. "battery": { "power": 800, "level": 76 }, "home": { "power": 1000, "today": 9.8 }}Showcase Template
Section titled “Showcase Template”ID: showcase
A picture of a thing, its state, when it last reported, and up to six readings: a car, a robot vacuum, a 3D printer, a plant.
Home Assistant
Section titled “Home Assistant”The Aivi integration sets up a Showcase widget from a form in Home Assistant, with a live preview. To use a plain automation instead, import the blueprint (Home Assistant 2025.7 or newer):
The picture needs a public URL that every phone can load; see Hosting Pictures.
API schema
Section titled “API schema”| Field | Type | Description | Required? |
|---|---|---|---|
template | "showcase" | Identifies this template. | Yes |
stats | Stat[] | 1–6 readings. Small shows the first, medium three, large four, and the portrait and iPad sizes all six, with the first number as the big reading. | Yes |
image | Picture | The picture of the thing. See Pictures. | No |
badge | Image | A small mark beside the report time, such as a brand logo. Not shown at the small size or with a cover picture. | No |
state | { icon, text } | The state: an optional icon (a symbol name or Icon) in front of text, a { value, text_color } word of 1–24 characters, for example a lock and Locked. | No |
reported_at | string | When the thing last reported, in ISO 8601 with a time zone, for example 2026-10-05T07:41:12Z. Shown as “2 min ago”. It doesn’t make the widget stale. | No |
icon | string | Icon | An SF Symbols icon name or an Icon. Shown in place of the picture on the Lock Screen and the Apple Watch. | No |
accent | Color | Palette color that themes the whole widget. Falls back to the icon color. | No |
tap_url | string | Tap action URL to open when the widget is tapped. | No |
Picture object
Section titled “Picture object”| Field | Type | Description | Required? |
|---|---|---|---|
url | string | An https:// URL, at most 2048 characters. | Yes |
alt | string | What the picture shows, at most 64 characters, for VoiceOver. Default: the widget’s name. | No |
fit | "contain" | "cover" | contain (the default) is a cut-out on a transparent background, shown whole. cover is a photo, cropped to fill its space. | No |
Stat object
Section titled “Stat object”| Field | Type | Description | Required? |
|---|---|---|---|
label | string | 1–24 characters, for example "Range". | Yes |
value | Value | The reading, for example "312". | Yes |
unit | string | Drawn after the value, for example "km". At most 8 characters. | No |
icon | string | An SF Symbol name that stands in for the label on the Lock Screen when the widget has no icon. | No |
max | number | Greater than 0. Draws the first stat as a gauge filled to value / max on the round Lock Screen widget. Needs a plain number as the value, such as 78 or -3.5, and no formatter. A 0–100 value with a % unit fills the gauge without it. | No |
Example payload
Section titled “Example payload”{ "template": "showcase", "image": { "url": "https://example.com/aivi/car-side.png", "alt": "The car" }, "badge": { "url": "https://example.com/aivi/brand.png", "alt": "Brand" }, "state": { "icon": "lock.fill", "text": { "value": "Locked", "text_color": "green" } }, "reported_at": "2026-10-05T07:41:12Z", "stats": [ { "label": "Battery", "value": { "value": "78" }, "unit": "%" }, { "label": "Range", "value": { "value": "312" }, "unit": "km", "icon": "road.lanes" }, { "label": "Odometer", "value": { "value": "24 180" }, "unit": "km" } ], "icon": "car.fill", "accent": "blue"}Switchboard Template
Section titled “Switchboard Template”ID: switchboard
A grid of up to eight pressable tiles, each a button (a scene, a script) or a toggle (a light, a fan). Every tile leads with a round mark that previews what the press does: a picture, a gradient of the scene’s colors, or a symbol on a color. Under it sit the label and a second line with the state or a subtitle such as the room. A tile fills with its mark’s color while it is on or active. A press on the Home Screen reaches your own system; Buttons and Toggles covers how presses travel, what a tile shows while it waits, and how you confirm the result.
Home Assistant
Section titled “Home Assistant”The Aivi integration turns your lights, switches, locks, garage doors, scenes and scripts into tiles, and presses reach Home Assistant with no webhook or open port. To build it with your own automations instead, the Buttons and Toggles page has a recipe that publishes a switchboard from your entities and turns presses into service calls, with a webhook and two automations. There is no blueprint for the Switchboard.
API schema
Section titled “API schema”| Field | Type | Description | Required? |
|---|---|---|---|
template | "switchboard" | Identifies this template. | Yes |
actions | Action[] | 1–8 actions, shown in the order you send them. Every id must be unique. | Yes |
icon | string | Icon | An SF Symbols icon name or an Icon. Shown on the Lock Screen and the Apple Watch. | No |
accent | Color | Palette color for tiles that set no color of their own. Falls back to the icon color. | No |
tap_url | string | Tap action URL to open when the widget is tapped outside a tile. | No |
Action object
Section titled “Action object”| Field | Type | Description | Required? |
|---|---|---|---|
id | string | What your system receives when the tile is pressed. Lowercase letters, digits, _ and -, starting with a letter or digit, at most 32 characters (^[a-z0-9][a-z0-9_-]{0,31}$). | Yes |
label | string | The tile’s label, 1–24 characters. | Yes |
subtitle | Value | A second line under the label, for example the room or “2700 K”. The same Value as the Budget and Countdown subtitles, so its text_color is a named color. | No |
icon | string | An SF Symbols name, 1–64 characters. A plain string: tiles draw their symbol in one color, so the Icon object’s options don’t apply. Always required, because some surfaces draw only the symbol (see Marks). | Yes |
color | Action color | The mark’s color behind the symbol, and the tile’s fill while it is on or active when there is no gradient or picture. Falls back to the widget’s accent. | No |
gradient | Action color[] | 2 or 3 stops, for example a scene’s light colors. Drawn as the mark in place of the symbol, and as the fill while the tile is on or active. | No |
image | Image | A picture drawn as the mark once the phone has it. See Pictures. | No |
kind | "button" | "toggle" | "link" | Default: "button". "link" is for the Remote only (see Links). | No |
url | string | Links only. The URL the tile opens, at most 256 characters. javascript, data, file, blob, about and aivi URLs are rejected. | For links |
active | bool | Buttons only. Highlights the tile, for example the scene that is running now. Only you set it; pressing never does. Default: false. | No |
on | bool | Toggles only. Whether the thing is on right now. A press asks for a change; your next update confirms it. | For toggles |
state_text | State words | Your own words for the tile’s states, such as “Open” and “Closed” for a garage door. Omit it for the app’s words. | No |
Any other field is rejected, including active on a toggle, on on a
button, and active, on and state_text on a link.
State words
Section titled “State words”The second line under a tile’s label names its state: On, Off,
Turning on, and so on. state_text replaces those words with yours,
one key per state. The keys depend on the action’s kind:
| Kind | Key | Shown when | The app’s word |
|---|---|---|---|
| toggle | on | The toggle is on. | On |
| toggle | off | The toggle is off. | Off |
| toggle | turning_on | A press to turn it on is waiting for your update. | Turning on |
| toggle | turning_off | A press to turn it off is waiting for your update. | Turning off |
| toggle | no_response | Your update didn’t arrive within 15 seconds. | No response |
| button | idle | The button is at rest. | (none) |
| button | active | You published the button with "active": true. | Active |
| button | sent | After a press, until your next update arrives. | Sent |
The words only rename the states: when each state shows is the same as without them, as described in What a tile shows.
- Leave a key out, or send
null, to keep the app’s word. A string is used as written, and""hides the word. - Each key stands alone. Renaming
ondoesn’t changeturning_on, so set every word you want changed. - One short line. Surrounding whitespace is trimmed, and a word can be at most 24 characters after trimming, with no line breaks, tabs, or other control characters.
- Keys match the kind. A toggle’s keys on a button, a button’s keys on
a toggle, and any other key are rejected with
422. - A hidden word leaves the subtitle. When the word is
"", the second line shows the subtitle alone, in every state, or stays empty without one. - VoiceOver still names the state. It reads your word, and the app’s word where you hid it, so on and off are always announced.
- Words count toward the 4 KiB content limit. Eight toggles with every word at its full 24 characters fit, but not together with rich marks on every tile.
A garage door that reads Open · Driveway, and Opening or Closing while a press waits:
{ "id": "garage", "label": "Garage door", "subtitle": { "value": "Driveway" }, "icon": "door.garage.open", "kind": "toggle", "on": true, "state_text": { "on": "Open", "off": "Closed", "turning_on": "Opening", "turning_off": "Closing" }}A lock:
{ "id": "front_door", "label": "Front door", "icon": "lock.fill", "kind": "toggle", "on": true, "state_text": { "on": "Locked", "off": "Unlocked", "turning_on": "Locking", "turning_off": "Unlocking" }}A scene button that reads Starting after a press and Running while you publish it as active:
{ "id": "evening", "label": "Evening", "icon": "sunset.fill", "active": true, "state_text": { "active": "Running", "sent": "Starting" }}And the smallest change, renaming only on and off:
{ "id": "vent", "label": "Vent", "icon": "wind", "kind": "toggle", "on": false, "state_text": { "on": "Open", "off": "Shut" }}Image object
Section titled “Image object”| Field | Type | Description | Required? |
|---|---|---|---|
url | string | An https:// URL with a host and no whitespace (^https://[^\s/?#]+[^\s]*$), at most 2048 characters. The phone fetches it; Aivi never downloads it. | Yes |
alt | string | What the picture shows, at most 64 characters. VoiceOver reads it after the label while the picture is drawn. | No |
Any other field in an Image is rejected.
Action colors
Section titled “Action colors”An action’s color and gradient stops take either a named
color or a hex color, #RRGGBB (six digits, no alpha,
no three-digit shorthand). Hex is stored and returned in upper case, so
#3d3dff comes back as #3D3DFF. Elsewhere in a widget, including its
accent and a subtitle’s text_color, colors are named only, and hex is
rejected with a 422.
A tile draws the richest mark it can:
- The picture from
image, once the phone has it. - Otherwise the gradient.
- Otherwise the symbol from
icon, oncolor(or the widget’saccent).
Send several, and the poorer ones stand in while a picture loads or when it can’t be shown. Surfaces that don’t show color always draw the symbol: the Lock Screen and Apple Watch families, the tinted and clear Home Screen styles, and the Always-On display. So does a locked phone that hides the widget’s content: it shows the symbol, never the picture.
The mark also says what the tile’s fill will be: a filled tile uses the
gradient, else the picture’s average color, else the color. Text on a
pale fill turns dark so it stays readable. A toggle that is off shows a
neutral tile with a muted mark, and a picture turns grey. The
Buttons and Toggles page shows
every state.
The actions fill a fixed grid in the order you send them. Slots you don’t use show as quiet empty tiles, so the grid keeps its shape. A size with fewer slots than actions shows the first ones.
| Size | Layout |
|---|---|
| Small | A 2 × 2 grid: the first four actions, each a mark over its label. |
| Medium | A 2 × 3 grid: the first six actions, each a mark beside its label and second line. |
| Large and extra large | The widget’s name on top, then a 2 × 4 grid of all eight: a mark beside the label and second line. |
| Extra large portrait (iOS 27) | The name, then a 2 × 4 grid of tall tiles: a large mark at the top, the label and second line at the bottom. |
| Rectangular (Lock Screen, Apple Watch) | The name, the symbols of the first four actions (lit when on or active), and how many are on. |
| Circular and inline (Lock Screen, Apple Watch) | The widget’s icon (a switch symbol by default) and how many are on. Inline adds the name. |
| Corner (Apple Watch) | How many are on, with the name along the edge. |
The second line shows the state (On, Off, Active, or your own state words) and the subtitle together when they fit (“On · 2700 K”), and the state alone when they don’t. An idle button shows its subtitle. While a press is on its way, the line shows its progress instead; see What a tile shows.
On the large sizes, the header shows the time of your last update next to the name once the content is stale, and otherwise only the name: the tiles already show what is on and what is on its way. The Lock Screen and Apple Watch summaries, a switchboard inside a Board, and VoiceOver count what is on instead: toggles that are on and buttons that are active. In the tinted and clear Home Screen styles, iOS draws the tiles in its own colors instead of yours.
Open All sizes on the preview above to see each size with the same content.
Example payload
Section titled “Example payload”The content the renders above show:
{ "template": "switchboard", "actions": [ { "id": "morning", "label": "Morning", "subtitle": { "value": "Kitchen" }, "icon": "sunrise.fill", "gradient": ["#FFF3C4", "#FFC46B"] }, { "id": "evening", "label": "Evening", "subtitle": { "value": "Living room" }, "icon": "sunset.fill", "gradient": ["#FF9A5A", "#FF4F8B"], "active": true }, { "id": "garage", "label": "Garage door", "subtitle": { "value": "Driveway" }, "icon": "door.garage.open", "kind": "toggle", "on": true, "color": "orange", "state_text": { "on": "Open", "off": "Closed", "turning_on": "Opening", "turning_off": "Closing" } }, { "id": "dog_cam", "label": "Dog cam", "subtitle": { "value": "Hallway" }, "icon": "video.fill", "kind": "toggle", "on": true, "gradient": ["#E8C07A", "#B98545"], "image": { "url": "https://getaivi.app/demo/switchboard/dog-cam.jpg", "alt": "The dog in the hallway" } }, { "id": "porch", "label": "Porch light", "subtitle": { "value": "2700 K" }, "icon": "lightbulb.fill", "kind": "toggle", "on": true, "gradient": ["#FFE4AD", "#FFAE5E"] }, { "id": "fountain", "label": "Fountain", "subtitle": { "value": "Garden" }, "icon": "drop.fill", "kind": "toggle", "on": false, "color": "#30B0C7" }, { "id": "cat_feeder", "label": "Cat feeder", "subtitle": { "value": "Kitchen" }, "icon": "pawprint.fill", "color": "brown", "image": { "url": "https://getaivi.app/demo/switchboard/cat-feeder.jpg", "alt": "The cat by its bowl" } }, { "id": "all_off", "label": "All off", "subtitle": { "value": "Whole home" }, "icon": "power", "color": "gray" } ]}Remote Template
Section titled “Remote Template”ID: remote
One device and its controls: a picture, one big reading, up to three stats, and up to five buttons and toggles you press on the Home Screen. A car’s lock and climate, a garage door, a robot vacuum. Presses work as on the Switchboard; see Buttons and Toggles.
API schema
Section titled “API schema”| Field | Type | Description | Required? |
|---|---|---|---|
template | "remote" | Identifies this template. | Yes |
value | Value | The big reading, for example a car’s charge. | Yes |
actions | Action[] | 1–5 buttons, toggles and links, the Switchboard’s action object. Every id must be unique. | Yes |
image | Picture | The picture of the device. See Pictures. | No |
stats | Stat[] | Up to 3 more readings, at the large size and up. | No |
reported_at | string | When the device last reported, as in the Showcase. | No |
icon | string | Icon | An SF Symbols icon name or an Icon. | No |
accent | Color | Palette color for the widget, and for buttons that set no color. Falls back to the icon color. | No |
tap_url | string | Tap action URL to open when the widget is tapped outside a button. | No |
There is no state field. On the Lock Screen and the Apple Watch, the widget names the first active button, or else the first toggle that is on, by its state word for that state, or its label.
- Buttons. Actions with no
image,gradientorcolorare drawn as plain buttons: symbols in a row in small and medium, a symbol over its label in large. Give any action one of those, and every action is drawn as a Switchboard tile with its label. - How many show. Small shows the first two actions, medium five (four as tiles), and large all five.
- Lock Screen and Apple Watch show the reading and that state, and can’t be pressed.
Example payload
Section titled “Example payload”{ "template": "remote", "image": { "url": "https://example.com/aivi/car-front.jpg", "fit": "cover" }, "reported_at": "2026-10-05T07:41:12Z", "value": { "value": "78%" }, "stats": [{ "label": "Range", "value": { "value": "312" }, "unit": "km" }], "actions": [ { "id": "lock", "kind": "toggle", "label": "Lock", "icon": "lock.fill", "on": true, "state_text": { "on": "Locked", "off": "Unlocked" } }, { "id": "climate", "kind": "toggle", "label": "Climate", "icon": "fan.fill", "on": false }, { "id": "trunk", "label": "Trunk", "icon": "car.side.rear.open.fill" } ], "icon": "car.fill", "accent": "blue"}Pictures
Section titled “Pictures”Some templates show pictures from a URL: the
Showcase’s picture and badge, the
Remote’s picture, an action’s image on the
Switchboard and Remote, and the
Remote Live Activity’s picture. Aivi never fetches or stores a picture: each phone downloads it,
shrinks it, and keeps it.
- Keep them small. PNG, JPEG, HEIC, WebP, or GIF (the first frame). An action’s picture can be at most 512 pixels on a side and 256 KB; any other picture at most 2048 pixels and 1 MB. A larger file is refused, and the widget shows the gradient, the symbol, or the icon instead. A URL that fails is not tried again for 30 minutes.
- A new picture can take a while. The Aivi app fetches pictures when it opens, after it syncs, and in the background a few times a day; a widget also fetches missing ones on its next update. Until then, it shows the gradient, the symbol, or the icon.
- Change the URL to change the picture. The phone keeps a picture for
24 hours without checking whether it changed. To show a new one sooner,
give it a new URL, for example
dog-cam.jpg?v=2. - Use a public, stable URL. Every phone showing the widget fetches it,
without your credentials. In Home Assistant, don’t forward an entity’s
entity_pictureas is: it is usually relative, authenticated, or changing. Hosting Pictures shows where to put your own. - Not on every widget surface. Lock Screen and Apple Watch widgets, the tinted and clear Home Screen styles, the Always-On display, and a widget that hides its content on a locked phone show the icon or symbol instead. A Live Activity shows its picture wherever it has room for one.
Publishing from Home Assistant
Section titled “Publishing from Home Assistant”The quickest path is the Aivi integration: choose the entities for each widget in a panel in Home Assistant, with a live preview and no YAML. If you prefer plain automations, the ready-made widget blueprints cover eleven of the templates, all but the Switchboard and the Remote, imported in a few clicks, with the payload plumbing handled for you. The Home Assistant widgets guide walks through the whole setup, from creating the widget to placing it on screen.
For anything the blueprints don’t cover, widgets use the same
RESTful command
pattern as Live Activities. Add a command for the
widget endpoint to configuration.yaml:
rest_command: update_widget: url: "https://api.getaivi.app/widgets/{{ slug }}" method: PATCH headers: content-type: application/json authorization: Token [token] payload: "{{ payload }}"Then call it from an automation whenever the source data changes. This example keeps a Gauge widget in sync with a temperature sensor:
- triggers: - trigger: state entity_id: sensor.living_room_temperature actions: - action: rest_command.update_widget data: slug: living-room payload: >- { "content": { "template": "gauge", "value": {{ states('sensor.living_room_temperature') | float(0) }}, "min_value": 10, "max_value": 35, "unit": "°C", "label": "Living room", "icon": "thermometer.medium" } }There is no need to update on a timer: widgets show the latest known state, so updating only when the value actually changes is enough. For slow-moving data (battery levels, prices), a time-pattern trigger every hour or a single nightly update works just as well.
Publishing from Shortcuts
Section titled “Publishing from Shortcuts”Shortcuts can update a widget when run manually, from the Action button, or on a schedule via a personal automation, no server required.
-
Add a Text action containing the request body:
{"content": {"template": "progress","progress": { "style": "simple", "value": 0.6, "color": "cyan" },"state": { "value": "1.5 of 2.5 L" },"icon": { "name": "drop.fill", "primary_color": "cyan" }}}Build the dynamic parts (numbers, dates) with earlier Shortcuts actions and insert them into the Text as variables.
-
Add a Get Contents of URL action pointing at
https://api.getaivi.app/widgets/water-intakeand expand Show More:- Method:
PATCH - Headers:
Authorization→Token YOUR_API_TOKENandContent-Type→application/json - Request Body: File, set to the Text from step 1
- Method:
-
Run the shortcut. The widget updates on every device that shows it.
For the full endpoint reference (request body, stale_after, responses, and
errors), see Update a widget in the API
reference.
Aivi: Live Activities and widgets over an API · Pricing · App Store