Skip to content

Activities

Activities are the core resource in PushWard. Each activity represents a trackable item that can be displayed as a Live Activity on subscribed devices.

Info

Each user can have a maximum of 25 activities. Attempting to create more returns 409 with {"error": "activity limit reached (max 25)"}.

Create Activity

POST /activities

Create a new activity. Starts in ENDED state. All user devices are automatically subscribed.

Request Body

FieldTypeRequiredDescription
slugstringYesURL-safe identifier, unique per user
namestringYesHuman-readable display name
priorityintegerNoEviction priority 0-10 (default: 0). Higher = kept longer.
ended_ttlintegerNoSeconds after ENDED transition before server auto-deletes the activity
stale_ttlintegerNoSeconds of inactivity while ONGOING before server auto-ends the activity
Example
curl -X POST https://api.pushward.app/activities \
  -H "Authorization: Bearer hla_YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "slug": "dishwasher",
    "name": "Dishwasher",
    "priority": 3
  }'

Response (201):

{
  "slug": "dishwasher",
  "name": "Dishwasher",
  "state": "ENDED",
  "priority": 3,
  "content": {},
  "ended_ttl": null,
  "stale_ttl": null,
  "delete_at": null,
  "created_at": "2025-06-15T10:30:00Z",
  "updated_at": "2025-06-15T10:30:00Z"
}

List Activities

GET /activities

List all activities for the current user, including activities shared with you. Returns an empty array if none exist.

Example
curl https://api.pushward.app/activities \
  -H "Authorization: Bearer hla_YOUR_TOKEN"

Response (200):

[
  {
    "slug": "dishwasher",
    "name": "Dishwasher",
    "state": "ONGOING",
    "priority": 3,
    "content": {
      "template": "generic",
      "progress": 0.65,
      "state": "Washing",
      "icon": "washer",
      "remaining_time": 1800,
      "subtitle": "Cycle 2 of 3",
      "accent_color": "blue"
    },
    "ended_ttl": null,
    "stale_ttl": null,
    "delete_at": null,
    "created_at": "2025-06-15T10:30:00Z",
    "updated_at": "2025-06-15T11:00:00Z"
  },
  {
    "slug": "ci-pipeline",
    "name": "CI Pipeline",
    "state": "ENDED",
    "priority": 0,
    "content": {},
    "ended_ttl": 3600,
    "stale_ttl": null,
    "delete_at": "2025-06-15T12:00:00Z",
    "created_at": "2025-06-15T10:00:00Z",
    "updated_at": "2025-06-15T11:00:00Z"
  },
  {
    "slug": "oven-timer",
    "name": "Oven Timer",
    "state": "ONGOING",
    "priority": 0,
    "content": {},
    "ended_ttl": null,
    "stale_ttl": null,
    "delete_at": null,
    "created_at": "2025-06-15T09:00:00Z",
    "updated_at": "2025-06-15T10:00:00Z",
    "is_shared": true,
    "share_role": "viewer",
    "owner_nickname": "Bob"
  }
]

Get Activity

GET /activities/{slug}

Get a single activity by slug.

Example
curl https://api.pushward.app/activities/dishwasher \
  -H "Authorization: Bearer hla_YOUR_TOKEN"

Delete Activity

DELETE /activities/{slug}

Delete an activity and all associated subscriptions.

Example
curl -X DELETE https://api.pushward.app/activities/dishwasher \
  -H "Authorization: Bearer hla_YOUR_TOKEN"

Response: 204 No Content

Update Activity (Primary Integration Endpoint)

PATCH /activity/{slug}

Update an activity's state and content. State transitions automatically trigger push notifications to subscribed devices.

Info

This is the main endpoint for integrations. Note the singular /activity/ path (not /activities/).

Request Body

FieldTypeRequiredDescription
statestringYes"ONGOING" or "ENDED"
contentobjectYesTemplate content (see Templates)
priorityintegerNoUpdate eviction priority (0-10)

State Transitions

FromToPush Action
ENDEDONGOINGPush-to-start (starts Live Activity)
ONGOINGONGOINGPush update (updates running activity)
ONGOINGENDEDPush end (dismisses after 4 hours)
PREEMPTEDONGOINGPush-to-start (restarts activity)
ENDEDENDEDNo push (no-op)
Example: Start a generic activity
curl -X PATCH https://api.pushward.app/activity/dishwasher \
  -H "Authorization: Bearer hla_YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "state": "ONGOING",
    "content": {
      "template": "generic",
      "progress": 0.65,
      "state": "Washing",
      "icon": "washer",
      "remaining_time": 1800,
      "subtitle": "Cycle 2 of 3",
      "accent_color": "blue"
    }
  }'

Response (200):

The response includes the updated activity object.

{
  "slug": "dishwasher",
  "name": "Dishwasher",
  "state": "ONGOING",
  "priority": 3,
  "content": {
    "template": "generic",
    "progress": 0.65,
    "state": "Washing",
    "icon": "washer",
    "remaining_time": 1800,
    "subtitle": "Cycle 2 of 3",
    "accent_color": "blue"
  },
  "ended_ttl": null,
  "stale_ttl": null,
  "delete_at": null,
  "created_at": "2025-06-15T10:30:00Z",
  "updated_at": "2025-06-15T11:00:00Z"
}

Content Object

The content object is shared across all templates. Two fields are always required:

FieldTypeDescription
templatestringRequired. One of: generic, countdown, steps, alert, gauge
progressfloatRequired. Value between 0.0 and 1.0

See the Templates section for template-specific fields.

Server-Side TTL

Activities support optional server-side time-to-live (TTL) for automatic lifecycle management:

FieldSet OnBehavior
stale_ttlCreateIf an ONGOING activity receives no updates for this many seconds, the server auto-ends it
ended_ttlCreateWhen an activity transitions to ENDED, the server schedules auto-deletion after this many seconds
delete_atAutoComputed timestamp (read-only). Set automatically from ended_ttl when state becomes ENDED
Info

When stale_ttl expires, the server auto-ends the activity with a distinct visual state: it sets content.state to "Stale (auto-ended)", accent_color to #8E8E93 (system gray), and icon to clock.badge.xmark. The activity then transitions to ENDED and a push-end notification is sent to all subscribed devices. If ended_ttl is also set, the auto-delete timer starts from that point.

💡 Tip

Use ended_ttl to let the server clean up finished activities automatically. Integration bridges typically set this on creation so activities don't accumulate indefinitely.

Error Responses

All errors return a JSON object with an error field:

{
  "error": "activity not found"
}
StatusMeaning
400Validation error (missing fields, invalid state, progress out of range)
401Missing or invalid token
403Integration key not allowed for this activity
404Activity not found
409Activity already exists or activity limit reached (on create)
429Rate limit exceeded