The state object
Every field in the state object that drives what the activity shows on the device.
state is the content shown on the device. On an update the fields sent are merged into the current state, so a call only carries what changes. That includes versus, which merges the same way one level down; segments is an array and is replaced whole.
| Field | Type | Default | Instructions |
|---|---|---|---|
layout | string | progress | The type: progress, picture, versus, or countdown. Set it on start; change it on an update to swap the layout live. |
primaryText | string | "" | The main headline. Set this on every activity. |
secondaryText | string | "" | A supporting line under the headline. On Countdown it is the short name shown in the expanded Dynamic Island. |
accessoryText | string | "" | A small badge or label. On Picture it is the eyebrow above the headline. |
accentColorHex | string | accent blue | The accent color as #RRGGBB. Drives the progress bar, badges, and tints. |
backgroundColorHex | string | none | The Lock Screen / tile surface color as #RRGGBB (Progress, Versus, Countdown). Text color is chosen automatically (black or white) for contrast. The Dynamic Island is always dark. |
backgroundGradientHex | string | none | A second color. Set it alongside backgroundColorHex to render an immersive top-to-bottom gradient instead of a flat surface (Versus, Countdown). |
backgroundImageUrl | string | none | A full-bleed background image (Countdown art, a Versus map, a Picture). A dark scrim is applied for legibility unless immersive is true. |
immersive | boolean | false | When true, the background image is shown without the scrim (art only, no overlaid text). |
headerIconUrl | string | none | The main image (a logo on Progress / Countdown, the photo on Picture). PNG transparency is preserved. |
lockScreenImageUrl | string | none | An optional Lock Screen variant of the image, for when the Lock Screen needs a different version than the dark Dynamic Island. Falls back to headerIconUrl. |
progress | number | 0 | Progress bar fill from 0.0 to 1.0. Clamped. Used by Progress. |
stage | string | none | A short label for the current step (for example "On the way"). |
estimatedMinutes | number | none | When greater than zero, shows a short "N min" label. Used by Progress. |
endIcon | string | none | An SF Symbol for the destination badge at the end of the progress bar. Used by Progress. iOS only. |
progressStyle | string | segmented | segmented (separate capsules) or linear (one continuous bar) when segments are present. |
segments | object[] | none | The segmented bar's steps. Each is { "label": string, "complete": boolean }. |
versus | object | none | The matchup object for the Versus type. See the versus object. |
countdownTo | number | none | Unix epoch seconds for the Countdown target. The clock ticks on the device. |
countdownStyle | string | auto | hms (00:00:00), days, hours, months, years, or auto. Used by Countdown. |
completionText | string | none | The copy shown in place of the clock once the countdown reaches countdownTo, and what the alert at that moment says. Falls back to secondaryText. Used by Countdown. |
actionLabel | string | none | Text for an interactive button (Versus "Watch Live", Picture "View Live"). |
actionURL | string | none | The URL the action button opens. |
actionColorHex | string | red | The action button's color as #RRGGBB. |
isComplete | boolean | false | Set to true for the final state. Shows a "Done" label and marks the activity finished. The activity stays on screen until it is ended. |
There is no text-color field: text is auto-set to black or white based on the surface so contrast is always correct (the Dynamic Island is always dark). Colors are #RRGGBB strings, and invalid values fall back to a default. endIcon is an SF Symbol name (like mappin.and.ellipse), not a URL; look one up in Apple's free SF Symbols app.
The versus object
Set this inside state when layout is versus.
| Field | Type | Default | Instructions |
|---|---|---|---|
leftName / rightName | string | "" | The two sides' names. |
leftScore / rightScore | string | "0" | Scores, sent as strings so "12", "$1,500", and "X" all work. |
leftStatus / rightStatus | string | "" | A status sub-label under each name (for example "Terrorist", "Winner"). |
activeSide | string | none | "left" or "right", marks the active side. |
leftColorHex / rightColorHex | string | blue / pink | Per-side accent colors as #RRGGBB. |
centerLabel | string | none | A small label in the middle column (for example "Map"). |
centerValue | string | none | The value under the center label (for example "Overpass"). |
leftImageUrl / rightImageUrl | string | none | The two team / side logos (transparent PNGs read best). |
centerImageUrl | string | none | An optional center image (an auction item, a map preview). |
sourceLogoUrl | string | none | The league / brand mark, shown in the bottom strip and centered on the Apple Watch tile. |
barColorHex | string | none | The bottom strip color on solid surfaces. |
The brand object
Optional, set at the top level of the start call. It carries the activity's own display name (title) and the branding shown on the activity and on the cover the user sees when they first open it. Change it later with POST /api/branding.
| Field | Type | Instructions |
|---|---|---|
title | string | The activity's display name, shown as the title of its row in the app. Not part of a running card's attributes, so a later change lands on the app's next refresh with no restart. |
name | string | The brand (source) name, shown on the activity (for example "Acme"). |
url | string | The brand site. The domain is shown under the brand name, and its favicon is the logo fallback. |
logo | string | A URL to the brand logo. If omitted, the app falls back to the state's headerIconUrl, then the site's favicon. |
description | string | A short description shown under the title on the activity's cover. |
color | string | The brand accent color as #RRGGBB. Seeds the state's accentColorHex unless the state sets its own. |
Errors
A refused call answers with a JSON body { statusCode, error, message }.
| Status | Message | Meaning |
|---|---|---|
| 400 | varies | A required field is missing or no target was named. |
| 401 | API_KEY_REQUIRED / INVALID_API_KEY | The x-api-key header is missing or wrong. |
| 402 | FREE_TIER_LIMIT_REACHED | The free tier's monthly call quota is used up. |
| 402 | BUTTON_STYLING_REQUIRES_PAID_PLAN | A button object on the free plan. |
| 404 | No activity matched | None of the named activityIds resolved. |









