# API Keys
Source: https://activitysmith.com/docs/api-keys
Create and manage API keys
API Keys are secret tokens used to authenticate your requests. They are unique to your account and should be kept confidential.
## Create an API Key
To create an API key, go to the [API Keys](https://activitysmith.com/app/keys) page and click the **Create API Key** button.
1. Click **Create API Key**.
2. Give your API Key a name (maximum 100 characters).
3. Select **All Recipients** or **Channel Scoped** as the Scope.
4. If you select **Channel Scoped**, you can choose the channels you want to restrict access to.
For security reasons, you can only view the API Key once.
## API Key Scopes
There are two different API Key Scopes you can choose from:
* **All Recipients**: The API key can target all recipients in the account.
* **Channel Scoped**: The API key is restricted to channels assigned to that key.
You can configure scope when creating or editing a key on the [API Keys](https://activitysmith.com/app/keys) page.
## Edit API Key details
After creating an API Key, you can edit the following details:
* Name
* Scope
* Scope Channels (only available if Scope is **Channel Scoped**)
To edit an API Key, click the **Edit API Key** button.
## View all API Keys
To manage your API keys, go to the [API Keys](https://activitysmith.com/app/keys) page. The page shows you all the API Keys you have created along with their details
## Scope behavior with `target.channels`
* Scope `all` + no `target.channels`: sends to all recipients in your account.
* Scope `all` + `target.channels`: sends only to the provided channel slugs.
* Scope `channels` + no `target.channels`: sends to channels assigned to that API key.
* Scope `channels` + `target.channels`: requested channel slugs must be a subset of channels assigned to that API key.
Channels in request payloads use channel slugs, for example, `["marketing", "test-devices"]`.
## Scope enforcement and errors
If a channel-scoped key (`channels`) tries to target channel slugs outside its assignment, the request fails with `403 Forbidden` and a clear scope-violation error message.
Use the [channels guide](/channels) for channel setup and targeting examples.
# End Live Activity
Source: https://activitysmith.com/docs/api-reference/endpoint/live-activity-end
POST /live-activity/end
Ends a Live Activity and archives its lifecycle. Supports segmented_progress, progress, metrics, and the legacy counter/timer/countdown step-based activity types. For segmented_progress activities, you can send the latest number_of_steps here if the workflow changed after start.
Use this endpoint to end an existing Live Activity by `activity_id`.
* `metrics`: send the final `metrics` array if you want the ended state to show final values.
* `segmented_progress`: send the final `current_step` and optionally the latest `number_of_steps`.
* `progress`: send the final `percentage`, or `value` with `upper_limit`.
* `action` is optional if you want the ended state to keep showing a button before dismissal.
* `auto_dismiss_minutes` is optional if you want to control how long the ended Live Activity stays visible.
For `segmented_progress`, the final payload can include an updated
`number_of_steps` as well as the final `current_step`. The step count does not
need to match the value used when the activity started.
# Start Live Activity
Source: https://activitysmith.com/docs/api-reference/endpoint/live-activity-start
POST /live-activity/start
Starts a Live Activity on devices matched by API key scope and optional target channels. Supports segmented_progress, progress, metrics, and the legacy counter/timer/countdown step-based activity types. For segmented_progress activities, number_of_steps can be changed later during update or end calls if the workflow changes.
Use this endpoint to create a new Live Activity and get back an `activity_id`.
You will use that same ID for later update and end calls.
* `metrics`: send `title`, `type`, and a non-empty `metrics` array.
* `segmented_progress`: send `title`, `type`, `number_of_steps`, and `current_step`.
* `progress`: send `title`, `type`, and either `percentage` or `value` with `upper_limit`.
* `action` is optional if you want the Live Activity to show one button that opens a URL or triggers a webhook.
* `target.channels` is optional if you want to scope delivery to specific channel slugs.
For `segmented_progress`, `number_of_steps` is not fixed for the full lifecycle.
You can change it later in update or end calls if the workflow adds or removes
steps.
# Update Live Activity
Source: https://activitysmith.com/docs/api-reference/endpoint/live-activity-update
POST /live-activity/update
Updates an existing Live Activity. If the per-activity token is not registered yet, the update is queued. Supports segmented_progress, progress, metrics, and the legacy counter/timer/countdown step-based activity types. For segmented_progress activities, you can increase or decrease number_of_steps here as the workflow changes.
Use this endpoint to update an existing Live Activity by `activity_id`.
* `metrics`: send the latest `metrics` array.
* `segmented_progress`: send the latest `current_step` and, if needed, an updated `number_of_steps`.
* `progress`: send the latest `percentage`, or `value` with `upper_limit`.
* `action` is optional if you want to set or replace the Live Activity button.
* `type` is optional on update if the existing Live Activity already has a type.
For `segmented_progress`, you can update both `current_step` and
`number_of_steps`. Use this when the workflow gains or loses steps after the
activity has already started.
# Send Push Notification
Source: https://activitysmith.com/docs/api-reference/endpoint/push-notification
POST /push-notification
Sends a push notification to devices matched by API key scope and optional target channels. Supports optional redirection URL, optional media preview or playback when the notification is expanded, and up to 4 interactive actions. `media` cannot be combined with `actions`.
# Introduction
Source: https://activitysmith.com/docs/api-reference/introduction
ActivitySmith API reference
## Features
Send a push notification to all of your paired devices.
Start, update and end a Live Activity.
## Base URL
All requests contain the following base URL:
```bash theme={null}
https://activitysmith.com/api/
```
## Authentication
For authentication, it's required to include an Authorization header. The header should contain `Bearer ask_123456789`, where `ask_123456789` represents your [API Key](/api-keys).
```bash theme={null}
Authorization: Bearer ask_123456789
```
## Response codes
ActivitySmith employs conventional HTTP status codes to signify the outcome of your requests.
Typically, 2xx HTTP status codes denote success, 4xx codes represent failures related to the user, and 5xx codes signal infrastructure problems.
| Status | Description |
| ------ | -------------------------------------------- |
| 200 | Request was successful. |
| 400 | Verify the correctness of the parameters. |
| 401 | The API key is invalid or was not provided. |
| 404 | The requested resource could not be located. |
| 429 | The rate limit has been surpassed. |
| 5xx | Signifies a server error with ActivitySmith. |
Refer to the Error Codes section for a detailed explanation of all potential API errors.
## Channels
Channels let you target specific subsets of recipients instead of broadcasting to everyone in an account.
* Use channels when teams, environments, or workflows need separate delivery groups.
* Keep [API keys](/api-keys) broad or channel-scoped depending on how strict you want targeting to be.
Read the full [channels guide](/channels).
## Rate limit
The ActivitySmith API has a [rate limit](/rate-limits) to ensure the stability and reliability of the service. The rate limit is applied to all endpoints and is based on the number of requests made within a specific time frame.
When you exceed the rate limit, you will receive a 429 response code.
# Channels
Source: https://activitysmith.com/docs/channels
Use channels to target specific users and devices.
Channels let you target specific groups of recipients instead of sending to everyone in an account.
Common use case:
* Different teams should receive different notifications (for example, `marketing`, `developers`, `devops`, `sre`).
Channels are flexible:
* You can assign users, devices, or both to a channel.
* You can choose channels per request using `target.channels`.
* You can keep some [API key](/api-keys) broad and scope other [API key](/api-keys) to specific channels.
## How Targeting Works
Use channel slugs in `target.channels`:
```json theme={null}
{
"target": {
"channels": ["ios-beta", "ops"]
}
}
```
* If `target` is omitted and API key scope is `all`, delivery goes to all recipients in the account.
* If `target` is omitted and API key scope is `channels`, delivery goes to channels assigned to that [API key](/api-keys).
* If `target.channels` is provided, only those channel slugs are targeted.
## Push Notifications
```json theme={null}
{
"title": "New subscription 💸",
"message": "Customer upgraded to Pro plan",
"target": {
"channels": ["ios-beta", "ops"]
}
}
```
## Live Activities
```json theme={null}
{
"content_state": {
"title": "Nightly database backup",
"number_of_steps": 3,
"current_step": 1,
"type": "segmented_progress"
},
"target": {
"channels": ["ios-beta", "ops"]
}
}
```
See endpoint docs for full request and response fields:
* [/api-reference/endpoint/push-notification](/api-reference/endpoint/push-notification)
* [/api-reference/endpoint/live-activity-start](/api-reference/endpoint/live-activity-start)
# Live Activity Colors
Source: https://activitysmith.com/docs/colors
Colors can be used to customize the appearance of your Live Activities. You can choose from the following colors(default is `blue`):
### `lime`
### `green`
### `cyan`
### `blue`
### `purple`
### `magenta`
### `red`
### `orange`
### `yellow`
# Quickstart
Source: https://activitysmith.com/docs/index
Send Live Activities and push notifications to your iOS devices from any backend
## Welcome to ActivitySmith
[ActivitySmith](https://activitysmith.com) is API service that lets you trigger and update Live Activities and send push notifications directly from your own infrastructure.
Pair your iOS device(s), authenticate with an [API key](/api-keys), and send real-time alerts from any backend, cron, agent, or automation. Without building or maintaining your own iOS app or dealing directly with APNs.
Check out the following resources to get started:
* **API**: [Documentation](https://activitysmith.com/docs/api-reference/introduction)
* **SDKs**: [Node](https://activitysmith.com/docs/sdks/node), [Python](https://activitysmith.com/docs/sdks/python), [Go](https://activitysmith.com/docs/sdks/go), [PHP](https://activitysmith.com/docs/sdks/php), [Ruby](https://activitysmith.com/docs/sdks/ruby), [CLI](https://activitysmith.com/docs/sdks/cli)
* **Others**: [Zapier](https://zapier.com/apps/activitysmith/integrations)
Want an SDK or Integration? Let us know at [adam@activitysmith.com](mailto:adam@activitysmith.com).
### Prerequisites
* [Create an API key](https://activitysmith.com/app/keys)
* [Download our iOS app](https://apps.apple.com/us/app/activitysmith/id6752254835) and pair your device(s)
### Features
* [**Push Notifications**](#push-notifications): send push notifications to all paired devices or target specific channels.
* [**Live Activities**](#live-activities): start, update and end a Live Activity on your lock screen or dynamic island, with optional channel targeting and one optional action button.
### Powerful Capabilities
* **Glanceable observability**: monitor real-time system state and long-running operations directly on your lock screen or dynamic island.
* **The hard stuff**: APNs(Apple Push Notification service), certificates, orchestration
* **Customizability**: adapt the experience to fit your unique needs.
* **Works with any backend**: use with any backend, cron, agent, automation or AI tool.
* **iOS app**: ready to use native iOS app for your iPhone or iPad. No need to build your own.
## Push Notifications
To send a push notification, use the `push-notification` endpoint. It takes `title` and optional fields like `message`, `payload`, `media`, `redirection`, `actions`, and `target.channels`.\
`target.channels` accepts channel slugs (for example `["devs", "ops"]`).
* `media` attaches an image, audio file, or video that users can preview or play when they expand the notification.
* `redirection` opens on normal tap.
* `actions` (up to 4) appear when users expand the notification.
* `media + redirection` is supported.
* `media + actions` is not supported in v1.
```bash cURL theme={null}
curl -X POST https://activitysmith.com/api/push-notification \
-H "Authorization: Bearer $ACTIVITYSMITH_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"title": "New subscription 💸",
"message": "Customer upgraded to Pro plan"
}'
```
```js Node theme={null}
import ActivitySmith from "activitysmith";
const activitysmith = new ActivitySmith({
apiKey: process.env.ACTIVITYSMITH_API_KEY,
});
await activitysmith.notifications.send({
title: "New subscription 💸",
message: "Customer upgraded to Pro plan",
});
```
```python Python theme={null}
import os
from activitysmith import ActivitySmith
activitysmith = ActivitySmith(
api_key=os.environ.get("ACTIVITYSMITH_API_KEY", "YOUR-API-KEY")
)
activitysmith.notifications.send(
{
"title": "New subscription 💸",
"message": "Customer upgraded to Pro plan",
}
)
```
```go Go theme={null}
package main
import (
"log"
"os"
activitysmithsdk "github.com/ActivitySmithHQ/activitysmith-go"
)
func main() {
activitysmith, err := activitysmithsdk.New(os.Getenv("ACTIVITYSMITH_API_KEY"))
if err != nil {
log.Fatal(err)
}
input := activitysmithsdk.PushNotificationInput{
Title: "New subscription 💸",
Message: "Customer upgraded to Pro plan",
}
_, err := activitysmith.Notifications.Send(input)
if err != nil {
log.Fatal(err)
}
}
```
```php PHP theme={null}
notifications->send([
'title' => 'New subscription 💸',
'message' => 'Customer upgraded to Pro plan',
]);
```
```ruby Ruby theme={null}
require "activitysmith"
activitysmith = ActivitySmith::Client.new(
api_key: ENV.fetch("ACTIVITYSMITH_API_KEY", "YOUR-API-KEY")
)
activitysmith.notifications.send(
{
title: "New subscription 💸",
message: "Customer upgraded to Pro plan"
}
)
```
```bash CLI theme={null}
activitysmith push \
--title "New subscription 💸" \
--message "Customer upgraded to Pro plan"
```
**Response**
```json theme={null}
{
"success": true,
"devices_notified": 3,
"users_notified": 1,
"timestamp": "2025-08-12T12:00:00.000Z"
}
```
## Live Activities
Live Activities come in two UI types, but the delivery flow stays the same:
start the activity, keep the returned `activity_id`, update it as state
changes, then end it when the work is done.
| Type | Best for | Required state |
| -------------------- | ----------------------------- | ------------------------------------------------------------------- |
| `segmented_progress` | Jobs tracked in steps | `title`, `type`, `number_of_steps`, `current_step` |
| `progress` | Jobs with continuous progress | `title`, `type`, and either `percentage` or `value` + `upper_limit` |
For `segmented_progress`, `number_of_steps` does not need to stay fixed. You
can increase or decrease it in later update or end calls if the workflow
changes.
Live Activities can also include one optional `action` button. Use `open_url`
when the button should open a browser destination, or `webhook` when
ActivitySmith backend should call your HTTPS webhook.
### Shared flow
1. Call [`POST /live-activity/start`](/api-reference/endpoint/live-activity-start).
2. Save the returned `activity_id`.
3. Call [`POST /live-activity/update`](/api-reference/endpoint/live-activity-update) whenever progress changes.
4. Call [`POST /live-activity/end`](/api-reference/endpoint/live-activity-end) when the work is finished.
### Start response
Starting a Live Activity returns the `activity_id` you will use for later
updates and for the final end call.
```json theme={null}
{
"success": true,
"activity_id": "pLAr-Hnq9ZFW4sxlk43Lhbuok4GLh7UW",
"devices_notified": 2,
"users_notified": 1,
"timestamp": "2026-01-28T09:57:22.929Z"
}
```
### Update and end response
Update and end calls return the same activity ID plus the delivery result.
```json theme={null}
{
"success": true,
"activity_id": "pLAr-Hnq9ZFW4sxlk43Lhbuok4GLh7UW",
"devices_notified": 2,
"timestamp": "2026-01-28T09:57:26.056Z"
}
```
### Segmented Progress Type
Use `segmented_progress` for jobs and workflows that move through clear steps or
phases. It fits jobs like backups, deployments, ETL pipelines, and checklists.
`number_of_steps` is dynamic, so you can increase or decrease it later if the
workflow changes.
#### Start
Start with `title`, `type: "segmented_progress"`, `number_of_steps`, and
`current_step`. Add `subtitle`, `color`, and `target.channels` if needed.
```bash cURL theme={null}
curl -X POST https://activitysmith.com/api/live-activity/start \
-H "Authorization: Bearer $ACTIVITYSMITH_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"content_state": {
"title": "Nightly database backup",
"subtitle": "create snapshot",
"number_of_steps": 3,
"current_step": 1,
"type": "segmented_progress",
"color": "yellow"
}
}'
```
```js Node theme={null}
import ActivitySmith from "activitysmith";
const activitysmith = new ActivitySmith({
apiKey: process.env.ACTIVITYSMITH_API_KEY,
});
const start = await activitysmith.liveActivities.start({
content_state: {
title: "Nightly database backup",
subtitle: "create snapshot",
number_of_steps: 3,
current_step: 1,
type: "segmented_progress",
color: "yellow",
},
});
const activityId = start.activity_id;
```
```python Python theme={null}
import os
from activitysmith import ActivitySmith
activitysmith = ActivitySmith(
api_key=os.environ.get("ACTIVITYSMITH_API_KEY", "YOUR-API-KEY")
)
start = activitysmith.live_activities.start(
{
"content_state": {
"title": "Nightly database backup",
"subtitle": "create snapshot",
"number_of_steps": 3,
"current_step": 1,
"type": "segmented_progress",
"color": "yellow",
}
}
)
activity_id = start.activity_id
```
```go Go theme={null}
package main
import (
"log"
"os"
activitysmithsdk "github.com/ActivitySmithHQ/activitysmith-go"
)
func main() {
activitysmith, err := activitysmithsdk.New(os.Getenv("ACTIVITYSMITH_API_KEY"))
if err != nil {
log.Fatal(err)
}
startInput := activitysmithsdk.LiveActivityStartInput{
Title: "Nightly database backup",
Subtitle: "create snapshot",
NumberOfSteps: 3,
CurrentStep: 1,
Type: "segmented_progress",
Color: "yellow",
}
start, err := activitysmith.LiveActivities.Start(startInput)
if err != nil {
log.Fatal(err)
}
activityID := start.GetActivityId()
log.Println(activityID)
}
```
```php PHP theme={null}
liveActivities->start([
'content_state' => [
'title' => 'Nightly database backup',
'subtitle' => 'create snapshot',
'number_of_steps' => 3,
'current_step' => 1,
'type' => 'segmented_progress',
'color' => 'yellow',
],
]);
$activityId = $start->getActivityId();
```
```ruby Ruby theme={null}
require "activitysmith"
activitysmith = ActivitySmith::Client.new(
api_key: ENV.fetch("ACTIVITYSMITH_API_KEY", "YOUR-API-KEY")
)
start = activitysmith.live_activities.start(
{
content_state: {
title: "Nightly database backup",
subtitle: "create snapshot",
number_of_steps: 3,
current_step: 1,
type: "segmented_progress",
color: "yellow"
}
}
)
activity_id = start.activity_id
```
```bash CLI theme={null}
activitysmith activity start \
--content-state '{"title":"Nightly database backup","subtitle":"create snapshot","numberOfSteps":3,"currentStep":1,"type":"segmented_progress","color":"yellow"}'
```
#### Update
Update the same activity with `activity_id` and the latest `current_step`. If
the workflow changes, you can also increase or decrease `number_of_steps`.
```bash cURL theme={null}
curl -X POST https://activitysmith.com/api/live-activity/update \
-H "Authorization: Bearer $ACTIVITYSMITH_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"activity_id": "pLAr-Hnq9ZFW4sxlk43Lhbuok4GLh7UW",
"content_state": {
"title": "Nightly database backup",
"subtitle": "upload archive",
"current_step": 2
}
}'
```
```js Node theme={null}
import ActivitySmith from "activitysmith";
const activitysmith = new ActivitySmith({
apiKey: process.env.ACTIVITYSMITH_API_KEY,
});
await activitysmith.liveActivities.update({
activity_id: "pLAr-Hnq9ZFW4sxlk43Lhbuok4GLh7UW",
content_state: {
title: "Nightly database backup",
subtitle: "upload archive",
current_step: 2,
},
});
```
```python Python theme={null}
import os
from activitysmith import ActivitySmith
activitysmith = ActivitySmith(
api_key=os.environ.get("ACTIVITYSMITH_API_KEY", "YOUR-API-KEY")
)
activitysmith.live_activities.update(
{
"activity_id": "pLAr-Hnq9ZFW4sxlk43Lhbuok4GLh7UW",
"content_state": {
"title": "Nightly database backup",
"subtitle": "upload archive",
"current_step": 2,
},
}
)
```
```go Go theme={null}
package main
import (
"log"
"os"
activitysmithsdk "github.com/ActivitySmithHQ/activitysmith-go"
)
func main() {
activitysmith, err := activitysmithsdk.New(os.Getenv("ACTIVITYSMITH_API_KEY"))
if err != nil {
log.Fatal(err)
}
updateInput := activitysmithsdk.LiveActivityUpdateInput{
ActivityID: "pLAr-Hnq9ZFW4sxlk43Lhbuok4GLh7UW",
Title: "Nightly database backup",
Subtitle: "upload archive",
CurrentStep: 2,
}
_, err = activitysmith.LiveActivities.Update(updateInput)
if err != nil {
log.Fatal(err)
}
}
```
```php PHP theme={null}
liveActivities->update([
'activity_id' => 'pLAr-Hnq9ZFW4sxlk43Lhbuok4GLh7UW',
'content_state' => [
'title' => 'Nightly database backup',
'subtitle' => 'upload archive',
'current_step' => 2,
],
]);
```
```ruby Ruby theme={null}
require "activitysmith"
activitysmith = ActivitySmith::Client.new(
api_key: ENV.fetch("ACTIVITYSMITH_API_KEY", "YOUR-API-KEY")
)
activitysmith.live_activities.update(
{
activity_id: "pLAr-Hnq9ZFW4sxlk43Lhbuok4GLh7UW",
content_state: {
title: "Nightly database backup",
subtitle: "upload archive",
current_step: 2
}
}
)
```
```bash CLI theme={null}
activitysmith activity update \
--activity-id "pLAr-Hnq9ZFW4sxlk43Lhbuok4GLh7UW" \
--content-state '{"title":"Nightly database backup","subtitle":"upload archive","currentStep":2}'
```
#### End
End the activity with the final state. You can send the latest
`current_step`, adjust `number_of_steps` if needed, and optionally set
`auto_dismiss_minutes`.
```bash cURL theme={null}
curl -X POST https://activitysmith.com/api/live-activity/end \
-H "Authorization: Bearer $ACTIVITYSMITH_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"activity_id": "pLAr-Hnq9ZFW4sxlk43Lhbuok4GLh7UW",
"content_state": {
"title": "Nightly database backup",
"subtitle": "verify restore",
"current_step": 3
}
}'
```
```js Node theme={null}
import ActivitySmith from "activitysmith";
const activitysmith = new ActivitySmith({
apiKey: process.env.ACTIVITYSMITH_API_KEY,
});
await activitysmith.liveActivities.end({
activity_id: "pLAr-Hnq9ZFW4sxlk43Lhbuok4GLh7UW",
content_state: {
title: "Nightly database backup",
subtitle: "verify restore",
current_step: 3,
auto_dismiss_minutes: 2,
},
});
```
```python Python theme={null}
import os
from activitysmith import ActivitySmith
activitysmith = ActivitySmith(
api_key=os.environ.get("ACTIVITYSMITH_API_KEY", "YOUR-API-KEY")
)
activitysmith.live_activities.end(
{
"activity_id": "pLAr-Hnq9ZFW4sxlk43Lhbuok4GLh7UW",
"content_state": {
"title": "Nightly database backup",
"subtitle": "verify restore",
"current_step": 3,
"auto_dismiss_minutes": 2,
},
}
)
```
```go Go theme={null}
package main
import (
"log"
"os"
activitysmithsdk "github.com/ActivitySmithHQ/activitysmith-go"
)
func main() {
activitysmith, err := activitysmithsdk.New(os.Getenv("ACTIVITYSMITH_API_KEY"))
if err != nil {
log.Fatal(err)
}
endInput := activitysmithsdk.LiveActivityEndInput{
ActivityID: "pLAr-Hnq9ZFW4sxlk43Lhbuok4GLh7UW",
Title: "Nightly database backup",
Subtitle: "verify restore",
CurrentStep: 3,
AutoDismissMinutes: 2,
}
_, err = activitysmith.LiveActivities.End(endInput)
if err != nil {
log.Fatal(err)
}
}
```
```php PHP theme={null}
liveActivities->end([
'activity_id' => 'pLAr-Hnq9ZFW4sxlk43Lhbuok4GLh7UW',
'content_state' => [
'title' => 'Nightly database backup',
'subtitle' => 'verify restore',
'current_step' => 3,
'auto_dismiss_minutes' => 2,
],
]);
```
```ruby Ruby theme={null}
require "activitysmith"
activitysmith = ActivitySmith::Client.new(
api_key: ENV.fetch("ACTIVITYSMITH_API_KEY", "YOUR-API-KEY")
)
activitysmith.live_activities.end(
{
activity_id: "pLAr-Hnq9ZFW4sxlk43Lhbuok4GLh7UW",
content_state: {
title: "Nightly database backup",
subtitle: "verify restore",
current_step: 3,
auto_dismiss_minutes: 2
}
}
)
```
```bash CLI theme={null}
activitysmith activity end \
--activity-id "pLAr-Hnq9ZFW4sxlk43Lhbuok4GLh7UW" \
--content-state '{"title":"Nightly database backup","subtitle":"verify restore","currentStep":3,"autoDismissMinutes":2}'
```
### Progress Type
Use `progress` when the state is naturally continuous. It fits charging,
downloads, sync jobs, uploads, timers, and any flow where a percentage or
numeric range is the clearest signal.
#### Start
Start with `title`, `type: "progress"`, and either `percentage` or `value` plus
`upper_limit`. Add `subtitle`, `color`, and `target.channels` if needed.
```bash cURL theme={null}
curl -X POST https://activitysmith.com/api/live-activity/start \
-H "Authorization: Bearer $ACTIVITYSMITH_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"content_state": {
"title": "EV Charging",
"subtitle": "Added 30 mi range",
"percentage": 15,
"type": "progress",
"color": "lime"
}
}'
```
```js Node theme={null}
import ActivitySmith from "activitysmith";
const activitysmith = new ActivitySmith({
apiKey: process.env.ACTIVITYSMITH_API_KEY,
});
const start = await activitysmith.liveActivities.start({
content_state: {
title: "EV Charging",
subtitle: "Added 30 mi range",
percentage: 15,
type: "progress",
color: "lime",
},
});
const activityId = start.activity_id;
```
```python Python theme={null}
import os
from activitysmith import ActivitySmith
activitysmith = ActivitySmith(
api_key=os.environ.get("ACTIVITYSMITH_API_KEY", "YOUR-API-KEY")
)
start = activitysmith.live_activities.start(
{
"content_state": {
"title": "EV Charging",
"subtitle": "Added 30 mi range",
"percentage": 15,
"type": "progress",
"color": "lime",
}
}
)
activity_id = start.activity_id
```
```go Go theme={null}
package main
import (
"log"
"os"
activitysmithsdk "github.com/ActivitySmithHQ/activitysmith-go"
)
func main() {
activitysmith, err := activitysmithsdk.New(os.Getenv("ACTIVITYSMITH_API_KEY"))
if err != nil {
log.Fatal(err)
}
startInput := activitysmithsdk.LiveActivityStartInput{
Title: "EV Charging",
Subtitle: "Added 30 mi range",
Percentage: 15,
Type: "progress",
Color: "lime",
}
start, err := activitysmith.LiveActivities.Start(startInput)
if err != nil {
log.Fatal(err)
}
activityID := start.GetActivityId()
log.Println(activityID)
}
```
```php PHP theme={null}
liveActivities->start([
'content_state' => [
'title' => 'EV Charging',
'subtitle' => 'Added 30 mi range',
'percentage' => 15,
'type' => 'progress',
'color' => 'lime',
],
]);
$activityId = $start->getActivityId();
```
```ruby Ruby theme={null}
require "activitysmith"
activitysmith = ActivitySmith::Client.new(
api_key: ENV.fetch("ACTIVITYSMITH_API_KEY", "YOUR-API-KEY")
)
start = activitysmith.live_activities.start(
{
content_state: {
title: "EV Charging",
subtitle: "Added 30 mi range",
percentage: 15,
type: "progress",
color: "lime"
}
}
)
activity_id = start.activity_id
```
```bash CLI theme={null}
activitysmith activity start \
--content-state '{"title":"EV Charging","subtitle":"Added 30 mi range","percentage":15,"type":"progress","color":"lime"}'
```
#### Update
Update the same activity with `activity_id` and the latest `percentage`, or
send `value` with `upper_limit`.
```bash cURL theme={null}
curl -X POST https://activitysmith.com/api/live-activity/update \
-H "Authorization: Bearer $ACTIVITYSMITH_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"activity_id": "pLAr-Hnq9ZFW4sxlk43Lhbuok4GLh7UW",
"content_state": {
"title": "EV Charging",
"subtitle": "Added 120 mi range",
"percentage": 60
}
}'
```
```js Node theme={null}
import ActivitySmith from "activitysmith";
const activitysmith = new ActivitySmith({
apiKey: process.env.ACTIVITYSMITH_API_KEY,
});
await activitysmith.liveActivities.update({
activity_id: "pLAr-Hnq9ZFW4sxlk43Lhbuok4GLh7UW",
content_state: {
title: "EV Charging",
subtitle: "Added 120 mi range",
percentage: 60,
},
});
```
```python Python theme={null}
import os
from activitysmith import ActivitySmith
activitysmith = ActivitySmith(
api_key=os.environ.get("ACTIVITYSMITH_API_KEY", "YOUR-API-KEY")
)
activitysmith.live_activities.update(
{
"activity_id": "pLAr-Hnq9ZFW4sxlk43Lhbuok4GLh7UW",
"content_state": {
"title": "EV Charging",
"subtitle": "Added 120 mi range",
"percentage": 60,
},
}
)
```
```go Go theme={null}
package main
import (
"log"
"os"
activitysmithsdk "github.com/ActivitySmithHQ/activitysmith-go"
)
func main() {
activitysmith, err := activitysmithsdk.New(os.Getenv("ACTIVITYSMITH_API_KEY"))
if err != nil {
log.Fatal(err)
}
updateInput := activitysmithsdk.LiveActivityUpdateInput{
ActivityID: "pLAr-Hnq9ZFW4sxlk43Lhbuok4GLh7UW",
Title: "EV Charging",
Subtitle: "Added 120 mi range",
Percentage: 60,
}
_, err = activitysmith.LiveActivities.Update(updateInput)
if err != nil {
log.Fatal(err)
}
}
```
```php PHP theme={null}
liveActivities->update([
'activity_id' => 'pLAr-Hnq9ZFW4sxlk43Lhbuok4GLh7UW',
'content_state' => [
'title' => 'EV Charging',
'subtitle' => 'Added 120 mi range',
'percentage' => 60,
],
]);
```
```ruby Ruby theme={null}
require "activitysmith"
activitysmith = ActivitySmith::Client.new(
api_key: ENV.fetch("ACTIVITYSMITH_API_KEY", "YOUR-API-KEY")
)
activitysmith.live_activities.update(
{
activity_id: "pLAr-Hnq9ZFW4sxlk43Lhbuok4GLh7UW",
content_state: {
title: "EV Charging",
subtitle: "Added 120 mi range",
percentage: 60
}
}
)
```
```bash CLI theme={null}
activitysmith activity update \
--activity-id "pLAr-Hnq9ZFW4sxlk43Lhbuok4GLh7UW" \
--content-state '{"title":"EV Charging","subtitle":"Added 120 mi range","percentage":60}'
```
#### End
End the activity with the final value. `auto_dismiss_minutes` is optional.
```bash cURL theme={null}
curl -X POST https://activitysmith.com/api/live-activity/end \
-H "Authorization: Bearer $ACTIVITYSMITH_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"activity_id": "pLAr-Hnq9ZFW4sxlk43Lhbuok4GLh7UW",
"content_state": {
"title": "EV Charging",
"subtitle": "Added 200 mi range",
"percentage": 100,
"auto_dismiss_minutes": 2
}
}'
```
```js Node theme={null}
import ActivitySmith from "activitysmith";
const activitysmith = new ActivitySmith({
apiKey: process.env.ACTIVITYSMITH_API_KEY,
});
await activitysmith.liveActivities.end({
activity_id: "pLAr-Hnq9ZFW4sxlk43Lhbuok4GLh7UW",
content_state: {
title: "EV Charging",
subtitle: "Added 200 mi range",
percentage: 100,
auto_dismiss_minutes: 2,
},
});
```
```python Python theme={null}
import os
from activitysmith import ActivitySmith
activitysmith = ActivitySmith(
api_key=os.environ.get("ACTIVITYSMITH_API_KEY", "YOUR-API-KEY")
)
activitysmith.live_activities.end(
{
"activity_id": "pLAr-Hnq9ZFW4sxlk43Lhbuok4GLh7UW",
"content_state": {
"title": "EV Charging",
"subtitle": "Added 200 mi range",
"percentage": 100,
"auto_dismiss_minutes": 2,
},
}
)
```
```go Go theme={null}
package main
import (
"log"
"os"
activitysmithsdk "github.com/ActivitySmithHQ/activitysmith-go"
)
func main() {
activitysmith, err := activitysmithsdk.New(os.Getenv("ACTIVITYSMITH_API_KEY"))
if err != nil {
log.Fatal(err)
}
endInput := activitysmithsdk.LiveActivityEndInput{
ActivityID: "pLAr-Hnq9ZFW4sxlk43Lhbuok4GLh7UW",
Title: "EV Charging",
Subtitle: "Added 200 mi range",
Percentage: 100,
AutoDismissMinutes: 2,
}
_, err = activitysmith.LiveActivities.End(endInput)
if err != nil {
log.Fatal(err)
}
}
```
```php PHP theme={null}
liveActivities->end([
'activity_id' => 'pLAr-Hnq9ZFW4sxlk43Lhbuok4GLh7UW',
'content_state' => [
'title' => 'EV Charging',
'subtitle' => 'Added 200 mi range',
'percentage' => 100,
'auto_dismiss_minutes' => 2,
],
]);
```
```ruby Ruby theme={null}
require "activitysmith"
activitysmith = ActivitySmith::Client.new(
api_key: ENV.fetch("ACTIVITYSMITH_API_KEY", "YOUR-API-KEY")
)
activitysmith.live_activities.end(
{
activity_id: "pLAr-Hnq9ZFW4sxlk43Lhbuok4GLh7UW",
content_state: {
title: "EV Charging",
subtitle: "Added 200 mi range",
percentage: 100,
auto_dismiss_minutes: 2
}
}
)
```
```bash CLI theme={null}
activitysmith activity end \
--activity-id "pLAr-Hnq9ZFW4sxlk43Lhbuok4GLh7UW" \
--content-state '{"title":"EV Charging","subtitle":"Added 200 mi range","percentage":100,"autoDismissMinutes":2}'
```
## Channel Targeting
You can scope delivery with channel slugs:
```json theme={null}
{
"target": {
"channels": ["devs", "ops"]
}
}
```
* If `target` is omitted and API key scope is `all`: send to all account recipients.
* If `target` is omitted and API key scope is `channels`: send to channels assigned to that key.
* If `target.channels` is present: only those channel slugs are used.
# Rate Limits
Source: https://activitysmith.com/docs/rate-limits
Rate limits for API requests
## Concurrent Live Activities Limits
Concurrent Live Activities represent how many live activities ActivitySmith can process for you at the same time.
Number of concurrent live activities is limited to 4. This limit mimics the number of concurrent live activities that can be processed by the iOS. If you exceed this limit, additional live activities will be ignored until resources become available.
In the future, we will allow you to increase this limit by upgrading to a paid plan.
If you require higher concurrency limits, please contact us at [adam@activitysmith.com](mailto:adam@activitysmith.com).
## API Rate Limits
Rate limits are measured in requests per minute and are primarily in place to prevent abuse. When configured correctly, your real bottleneck will be concurrent live activities.
**Shared account limit**: 60 requests per minute across these endpoints:
* `POST /push-notification`
* `POST /live-activity/start`
* `POST /live-activity/update`
* `POST /live-activity/end`
These rate limits are enforced to ensure fair usage and availability of the API for all users. If you require higher limits, please contact us at [adam@activitysmith.com](mailto:adam@activitysmith.com).
# CLI
Source: https://activitysmith.com/docs/sdks/cli
Use the ActivitySmith CLI to send push notifications and manage Live Activity progress from shell scripts, CI pipelines, and terminal workflows.
## Installation
Install the ActivitySmith CLI globally with npm:
```bash CLI theme={null}
# Install globally with npm
npm install -g activitysmith-cli
```
## Usage
1. [Create an API key](https://activitysmith.com/app/keys)
2. Authenticate with `ACTIVITYSMITH_API_KEY` or pass `--api-key` per command.
3. Run `activitysmith --help` to inspect available commands.
Use the environment variable when you want the cleanest shell scripts:
```bash CLI theme={null}
export ACTIVITYSMITH_API_KEY="YOUR-API-KEY"
activitysmith --help
```
Or pass the key directly:
```bash CLI theme={null}
activitysmith --api-key "YOUR-API-KEY" push --title "Hello"
```
## Commands
### Push Notification
Use `activitysmith push` when a deploy fails, a CI run finishes, or a background job needs attention.
```bash CLI theme={null}
activitysmith push \
--title "Build Failed 🚨" \
--message "CI pipeline failed on main branch" \
--subtitle "main"
```
### Rich Push Notifications with Media
```bash CLI theme={null}
activitysmith push \
--title "Homepage ready" \
--message "Your agent finished the redesign." \
--media "https://cdn.example.com/output/homepage-v2.png" \
--redirection "https://github.com/acme/web/pull/482"
```
Send images, videos, or audio with your push notifications, press and hold to preview media directly from the notification, then tap through to open the linked content.
What will work:
* direct image URL: `.jpg`, `.png`, `.gif`, etc.
* direct audio file URL: `.mp3`, `.m4a`, etc.
* direct video file URL: `.mp4`, `.mov`, etc.
* URL that responds with a proper media `Content-Type`, even if the path has no extension
`--media` can be combined with `--redirection`, but not with `--actions`.
### Actionable Push Notifications
Push notification redirection and actions are optional and can be used to redirect the user to a specific URL when they tap the notification or to trigger a specific action when they long-press the notification. Webhooks are executed by ActivitySmith backend.
```bash CLI theme={null}
activitysmith push \
--title "Build Failed 🚨" \
--message "CI pipeline failed on main branch" \
--redirection "https://github.com/org/repo/actions/runs/123456789" \
--actions '[
{
"title": "Open Failing Run",
"type": "open_url",
"url": "https://github.com/org/repo/actions/runs/123456789"
},
{
"title": "Create Incident",
"type": "webhook",
"url": "https://hooks.example.com/incidents/create",
"method": "POST",
"body": {
"service": "payments-api",
"severity": "high",
"source": "activitysmith-cli"
}
}
]'
```
You can also load actions from a file:
```bash CLI theme={null}
activitysmith push \
--title "Build Failed 🚨" \
--message "CI pipeline failed on main branch" \
--actions-file "./actions.json"
```
## Live Activities
There are three types of Live Activities:
* `metrics`: best for live operational stats like server CPU and memory, queue depth, or replica lag
* `segmented_progress`: best for step-based workflows like deployments, backups, and ETL pipelines
* `progress`: best for continuous jobs like uploads, reindexes, and long-running migrations tracked as a percentage
When working with Live Activities via our API, you have two approaches tailored
to different needs. First, the stateless mode is the simplest path - one API
call can initiate or update an activity, and another ends it - no state
tracking on your side.
This is ideal if you want minimal complexity, perfect for automated workflows
like cron jobs.
In contrast, if you need precise lifecycle control, the classic approach offers
distinct calls for start, updates, and end, giving you full control over the
activity's state.
In the following sections, we'll break down how to implement each method so you
can choose what fits your use case best.
### Simple: Let ActivitySmith manage the Live Activity for you
Use a stable `stream_key` to identify the system or workflow you are tracking,
such as a server, deployment, build pipeline, cron job, or charging session.
This is especially useful for cron jobs and other scheduled tasks where you do
not want to store `activity_id` between runs.
#### Metrics
```bash theme={null}
activitysmith activity stream prod-web-1 \
--content-state '{
"title": "Server Health",
"subtitle": "prod-web-1",
"type": "metrics",
"metrics": [
{ "label": "CPU", "value": 9, "unit": "%" },
{ "label": "MEM", "value": 45, "unit": "%" }
]
}'
```
#### Segmented progress
```bash theme={null}
activitysmith activity stream nightly-backup \
--content-state '{
"title": "Nightly Backup",
"subtitle": "upload archive",
"type": "segmented_progress",
"numberOfSteps": 3,
"currentStep": 2
}'
```
#### Progress
```bash theme={null}
activitysmith activity stream search-reindex \
--content-state '{
"title": "Search Reindex",
"subtitle": "catalog-v2",
"type": "progress",
"percentage": 42
}'
```
Run `activitysmith activity stream ...` again with the same
`stream_key` whenever the state changes.
#### End a stream
Use this when the tracked process is finished and you no longer want the Live
Activity on devices. `content_state` is optional here; include it if you want
to end the stream with a final state.
```bash theme={null}
activitysmith activity end-stream prod-web-1 \
--content-state '{
"title": "Server Health",
"subtitle": "prod-web-1",
"type": "metrics",
"metrics": [
{ "label": "CPU", "value": 7, "unit": "%" },
{ "label": "MEM", "value": 38, "unit": "%" }
]
}'
```
If you later send another `activity stream` request with the same `stream_key`,
ActivitySmith starts a new Live Activity for that stream again.
Stream responses include an `operation` field:
* `started`: ActivitySmith started a new Live Activity for this `stream_key`
* `updated`: ActivitySmith updated the current Live Activity
* `rotated`: ActivitySmith ended the previous Live Activity and started a new one
* `noop`: the incoming state matched the current state, so no update was sent
* `paused`: the stream is paused, so no Live Activity was started or updated
* `ended`: returned by `activity end-stream` after the stream is ended
### Advanced: Full lifecycle control
Use these commands when you want to manage the Live Activity lifecycle yourself:
1. Run `activitysmith activity start ...`.
2. Save the returned `activity_id`.
3. Run `activitysmith activity update ...` as progress changes.
4. Run `activitysmith activity end ...` when the work is finished.
You can use `--content-state ` for the examples below, or build the same
payload with flags as documented in `Content State Options`.
### Metrics Type
Use `metrics` when you want to keep a small set of live stats visible, such as
server health, queue pressure, or database load.
#### Start
```bash theme={null}
activitysmith activity start \
--content-state '{
"title": "Server Health",
"subtitle": "prod-web-1",
"type": "metrics",
"metrics": [
{ "label": "CPU", "value": 9, "unit": "%" },
{ "label": "MEM", "value": 45, "unit": "%" }
]
}'
```
#### Update
```bash theme={null}
activitysmith activity update \
--activity-id "" \
--content-state '{
"title": "Server Health",
"subtitle": "prod-web-1",
"type": "metrics",
"metrics": [
{ "label": "CPU", "value": 76, "unit": "%" },
{ "label": "MEM", "value": 52, "unit": "%" }
]
}'
```
#### End
```bash theme={null}
activitysmith activity end \
--activity-id "" \
--content-state '{
"title": "Server Health",
"subtitle": "prod-web-1",
"type": "metrics",
"metrics": [
{ "label": "CPU", "value": 7, "unit": "%" },
{ "label": "MEM", "value": 38, "unit": "%" }
],
"autoDismissMinutes": 2
}'
```
### Segmented Progress Type
Use `segmented_progress` for jobs and workflows that move through clear steps or
phases. It fits jobs like deployments, backups, ETL pipelines, and checklists.
`numberOfSteps` is dynamic, so you can increase or decrease it later if the
workflow changes.
#### Start
```bash theme={null}
activitysmith activity start \
--content-state '{
"title": "Nightly database backup",
"subtitle": "create snapshot",
"numberOfSteps": 3,
"currentStep": 1,
"type": "segmented_progress",
"color": "yellow"
}'
```
#### Update
```bash theme={null}
activitysmith activity update \
--activity-id "" \
--content-state '{
"title": "Nightly database backup",
"subtitle": "upload archive",
"numberOfSteps": 3,
"currentStep": 2
}'
```
#### End
```bash theme={null}
activitysmith activity end \
--activity-id "" \
--content-state '{
"title": "Nightly database backup",
"subtitle": "verify restore",
"numberOfSteps": 3,
"currentStep": 3,
"autoDismissMinutes": 2
}'
```
### Progress Type
Use `progress` when the state is naturally continuous. It fits charging,
downloads, sync jobs, uploads, timers, and any flow where a percentage or
numeric range is the clearest signal.
#### Start
```bash theme={null}
activitysmith activity start \
--content-state '{
"title": "EV Charging",
"subtitle": "Added 30 mi range",
"type": "progress",
"percentage": 15
}'
```
#### Update
```bash theme={null}
activitysmith activity update \
--activity-id "" \
--content-state '{
"title": "EV Charging",
"subtitle": "Added 120 mi range",
"percentage": 60
}'
```
#### End
```bash theme={null}
activitysmith activity end \
--activity-id "" \
--content-state '{
"title": "EV Charging",
"subtitle": "Added 200 mi range",
"percentage": 100,
"autoDismissMinutes": 2
}'
```
### Live Activity Action
Just like Actionable Push Notifications, Live Activities can have a button that opens provided URL in a browser or triggers a webhook. Webhooks are executed by the ActivitySmith backend.
#### Open URL action
```bash CLI theme={null}
activitysmith activity start \
--content-state '{
"title": "Deploying payments-api",
"subtitle": "Running database migrations",
"numberOfSteps": 5,
"currentStep": 3,
"type": "segmented_progress"
}' \
--action '{
"title": "Open Workflow",
"type": "open_url",
"url": "https://github.com/acme/payments-api/actions/runs/1234567890"
}'
```
#### Webhook action
```bash CLI theme={null}
activitysmith activity update \
--activity-id "" \
--content-state '{
"title": "Reindexing product search",
"subtitle": "Shard 7 of 12",
"numberOfSteps": 12,
"currentStep": 7
}' \
--action '{
"title": "Pause Reindex",
"type": "webhook",
"url": "https://ops.example.com/hooks/search/reindex/pause",
"method": "POST",
"body": {
"job_id": "reindex-2026-03-19",
"requested_by": "activitysmith-cli"
}
}'
```
## Content State
For `activity start`, `activity update`, and `activity end`, you can pass content state in two ways:
* JSON: `--content-state '{...}'` or `--content-state-file path/to/payload.json`
* Flags: `--title`, `--subtitle`, `--type`, `--number-of-steps`, `--current-step`, `--percentage`, `--value`, `--upper-limit`, `--color`, `--step-color`, `--auto-dismiss-minutes`
Live Activity action options:
* `--action '{...}'`
* `--action-file path/to/action.json`
Use camelCase keys in JSON, for example `numberOfSteps`, `currentStep`, and `autoDismissMinutes`.
When the payload gets long, format the JSON across multiple lines or move it to `--content-state-file`.
### Required Fields
* `activity start`: `--title`, `--type`, plus either `--number-of-steps` and `--current-step`, or `--percentage`, or `--value` with `--upper-limit`
* `activity update`: `--title`, plus either `--current-step`, or `--percentage`, or `--value` with `--upper-limit`
* `activity end`: `--title`, plus either `--current-step`, or `--percentage`, or `--value` with `--upper-limit`
### Field Reference
| Field | Description |
| -------------------- | ----------------------------------------------------------------------------------------------- |
| `title` | Primary label shown in the Live Activity. |
| `subtitle` | Secondary label shown in the Live Activity. |
| `type` | Layout type: `segmented_progress` or `progress`. |
| `numberOfSteps` | Total steps in a segmented progress flow. |
| `currentStep` | Current step index for segmented progress, starting at `1`. |
| `percentage` | Progress value from `0` to `100`. |
| `value` | Current numeric progress value when you want a custom range. |
| `upperLimit` | Maximum numeric progress value used with `value`. |
| `color` | Accent color (`lime`, `green`, `cyan`, `blue`, `purple`, `magenta`, `red`, `orange`, `yellow`). |
| `stepColor` | Segment color for `segmented_progress` (same values as `color`). |
| `autoDismissMinutes` | Minutes before dismissal after ending. Default `3`, use `0` for immediate dismissal. |
## Channels
Use `--channels` to target specific channels for `push` and `activity start`.
```bash CLI theme={null}
activitysmith push \
--title "Build Failed 🚨" \
--message "CI pipeline failed on main branch" \
--channels "devs,ops"
```
```bash CLI theme={null}
activitysmith activity start \
--title "Nightly database backup" \
--type segmented_progress \
--number-of-steps 4 \
--current-step 1 \
--channels "devs,ops"
```
## Output
Use `--json` for machine-readable output:
```bash CLI theme={null}
activitysmith push --title "Hello" --json
```
## Error Handling
The CLI exits non-zero on non-2xx responses and prints the API error body. That includes validation failures, rate limits, and Live Activity limit errors.
## Additional Resources
Install the ActivitySmith CLI from npm
View the CLI source on GitHub
# Go
Source: https://activitysmith.com/docs/sdks/go
Use the ActivitySmith Go SDK to send push notifications and Live Activity progress updates from Go services, workers, and automation jobs.
## Installation
Install the ActivitySmith Go SDK with `go get`:
```go Go theme={null}
go get github.com/ActivitySmithHQ/activitysmith-go
```
## Usage
1. [Create an API key](https://activitysmith.com/app/keys)
2. Pass the API key into `activitysmithsdk.New`.
3. Reuse the client anywhere you send pushes or Live Activity updates.
Create the client once:
```go Go theme={null}
package main
import (
"log"
activitysmithsdk "github.com/ActivitySmithHQ/activitysmith-go"
)
func main() {
activitysmith, err := activitysmithsdk.New("YOUR-API-KEY")
if err != nil {
log.Fatal(err)
}
}
```
### Send a Push Notification
Use `activitysmith.Notifications.Send` when a deploy finishes, a customer upgrades, or a background job needs attention.
```go Go theme={null}
input := activitysmithsdk.PushNotificationInput{
Title: "New subscription 💸",
Message: "Customer upgraded to Pro plan",
}
_, err := activitysmith.Notifications.
Send(input)
if err != nil {
log.Fatal(err)
}
```
### Rich Push Notifications with Media
```go Go theme={null}
input := activitysmithsdk.PushNotificationInput{
Title: "Homepage ready",
Message: "Your agent finished the redesign.",
Media: "https://cdn.example.com/output/homepage-v2.png",
Redirection: "https://github.com/acme/web/pull/482",
}
_, err := activitysmith.Notifications.
Send(input)
if err != nil {
log.Fatal(err)
}
```
Send images, videos, or audio with your push notifications, press and hold to preview media directly from the notification, then tap through to open the linked content.
What will work:
* direct image URL: `.jpg`, `.png`, `.gif`, etc.
* direct audio file URL: `.mp3`, `.m4a`, etc.
* direct video file URL: `.mp4`, `.mov`, etc.
* URL that responds with a proper media `Content-Type`, even if the path has no extension
`Media` can be combined with `Redirection`, but not with `Actions`.
### Actionable Push Notifications
Push notification redirection and actions are optional and can be used to redirect the user to a specific URL when they tap the notification or to trigger a specific action when they long-press the notification. Webhooks are executed by ActivitySmith backend.
```go Go theme={null}
import (
"log"
activitysmithsdk "github.com/ActivitySmithHQ/activitysmith-go"
"github.com/ActivitySmithHQ/activitysmith-go/generated"
)
crmAction := *generated.NewPushNotificationAction(
"Open CRM Profile",
generated.PUSHNOTIFICATIONACTIONTYPE_OPEN_URL,
"https://crm.example.com/customers/cus_9f3a1d",
)
onboardingAction := *generated.NewPushNotificationAction(
"Start Onboarding Workflow",
generated.PUSHNOTIFICATIONACTIONTYPE_WEBHOOK,
"https://hooks.example.com/activitysmith/onboarding/start",
)
onboardingAction.SetMethod(generated.PUSHNOTIFICATIONACTIONMETHOD_POST)
onboardingAction.SetBody(map[string]interface{}{
"customer_id": "cus_9f3a1d",
"plan": "pro",
})
input := activitysmithsdk.PushNotificationInput{
Title: "New subscription 💸",
Message: "Customer upgraded to Pro plan",
Redirection: "https://crm.example.com/customers/cus_9f3a1d",
Actions: []generated.PushNotificationAction{
crmAction,
onboardingAction,
},
}
_, err := activitysmith.Notifications.
Send(input)
if err != nil {
log.Fatal(err)
}
```
## Live Activities
There are three types of Live Activities:
* `metrics`: best for live operational stats like server CPU and memory, queue depth, or replica lag
* `segmented_progress`: best for step-based workflows like deployments, backups, and ETL pipelines
* `progress`: best for continuous jobs like uploads, reindexes, and long-running migrations tracked as a percentage
When working with Live Activities via our API, you have two approaches tailored
to different needs. First, the stateless mode is the simplest path - one API
call can initiate or update an activity, and another ends it - no state
tracking on your side.
This is ideal if you want minimal complexity, perfect for automated workflows
like cron jobs.
In contrast, if you need precise lifecycle control, the classic approach offers
distinct calls for start, updates, and end, giving you full control over the
activity's state.
In the following sections, we'll break down how to implement each method so you
can choose what fits your use case best.
### Simple: Let ActivitySmith manage the Live Activity for you
Use a stable `streamKey` to identify the system or workflow you are tracking,
such as a server, deployment, build pipeline, cron job, or charging session.
This is especially useful for cron jobs and other scheduled tasks where you do
not want to store `activityID` between runs.
#### Metrics
```go theme={null}
streamInput := activitysmithsdk.LiveActivityStreamInput{
Title: "Server Health",
Subtitle: "prod-web-1",
Type: "metrics",
Metrics: []generated.ActivityMetric{
{Label: "CPU", Value: 9, Unit: generated.PtrString("%")},
{Label: "MEM", Value: 45, Unit: generated.PtrString("%")},
},
}
status, err := activitysmith.LiveActivities.Stream("prod-web-1", streamInput)
if err != nil {
log.Fatal(err)
}
```
#### Segmented progress
```go theme={null}
streamInput := activitysmithsdk.LiveActivityStreamInput{
Title: "Nightly Backup",
Subtitle: "upload archive",
Type: "segmented_progress",
NumberOfSteps: 3,
CurrentStep: 2,
}
_, err := activitysmith.LiveActivities.Stream("nightly-backup", streamInput)
if err != nil {
log.Fatal(err)
}
```
#### Progress
```go theme={null}
streamInput := activitysmithsdk.LiveActivityStreamInput{
Title: "Search Reindex",
Subtitle: "catalog-v2",
Type: "progress",
Percentage: 42,
}
_, err := activitysmith.LiveActivities.Stream("search-reindex", streamInput)
if err != nil {
log.Fatal(err)
}
```
Call `Stream(...)` again with the same `streamKey` whenever the state changes.
#### End a stream
Use this when the tracked process is finished and you no longer want the Live
Activity on devices. `content_state` is optional here; include it if you want
to end the stream with a final state.
```go theme={null}
endInput := activitysmithsdk.LiveActivityStreamEndInput{
Title: "Server Health",
Subtitle: "prod-web-1",
Type: "metrics",
Metrics: []generated.ActivityMetric{
{Label: "CPU", Value: 7, Unit: generated.PtrString("%")},
{Label: "MEM", Value: 38, Unit: generated.PtrString("%")},
},
}
_, err := activitysmith.LiveActivities.EndStream("prod-web-1", endInput)
if err != nil {
log.Fatal(err)
}
```
If you later send another `Stream(...)` request with the same `streamKey`,
ActivitySmith starts a new Live Activity for that stream again.
Stream responses include an `Operation` field:
* `started`: ActivitySmith started a new Live Activity for this `streamKey`
* `updated`: ActivitySmith updated the current Live Activity
* `rotated`: ActivitySmith ended the previous Live Activity and started a new one
* `noop`: the incoming state matched the current state, so no update was sent
* `paused`: the stream is paused, so no Live Activity was started or updated
* `ended`: returned by `EndStream(...)` after the stream is ended
### Advanced: Full lifecycle control
Use these methods when you want to manage the Live Activity lifecycle yourself:
1. Call `activitysmith.LiveActivities.Start(...)`.
2. Save the returned `activityID`.
3. Call `activitysmith.LiveActivities.Update(...)` as progress changes.
4. Call `activitysmith.LiveActivities.End(...)` when the work is finished.
### Metrics Type
Use `metrics` when you want to keep a small set of live stats visible, such as
server health, queue pressure, or database load.
#### Start
```go theme={null}
startInput := activitysmithsdk.LiveActivityStartInput{
Title: "Server Health",
Subtitle: "prod-web-1",
Type: "metrics",
Metrics: []generated.ActivityMetric{
{Label: "CPU", Value: 9, Unit: generated.PtrString("%")},
{Label: "MEM", Value: 45, Unit: generated.PtrString("%")},
},
}
start, err := activitysmith.LiveActivities.Start(startInput)
if err != nil {
log.Fatal(err)
}
activityID := start.GetActivityId()
```
#### Update
```go theme={null}
updateInput := activitysmithsdk.LiveActivityUpdateInput{
ActivityID: activityID,
Title: "Server Health",
Subtitle: "prod-web-1",
Type: "metrics",
Metrics: []generated.ActivityMetric{
{Label: "CPU", Value: 76, Unit: generated.PtrString("%")},
{Label: "MEM", Value: 52, Unit: generated.PtrString("%")},
},
}
_, err := activitysmith.LiveActivities.Update(updateInput)
if err != nil {
log.Fatal(err)
}
```
#### End
```go theme={null}
endInput := activitysmithsdk.LiveActivityEndInput{
ActivityID: activityID,
Title: "Server Health",
Subtitle: "prod-web-1",
Type: "metrics",
Metrics: []generated.ActivityMetric{
{Label: "CPU", Value: 7, Unit: generated.PtrString("%")},
{Label: "MEM", Value: 38, Unit: generated.PtrString("%")},
},
AutoDismissMinutes: 2,
}
_, err := activitysmith.LiveActivities.End(endInput)
if err != nil {
log.Fatal(err)
}
```
### Segmented Progress Type
Use `segmented_progress` for jobs and workflows that move through clear steps or
phases. It fits jobs like backups, deployments, ETL pipelines, and checklists.
`NumberOfSteps` is dynamic, so you can increase or decrease it later if the
workflow changes.
#### Start
```go theme={null}
startInput := activitysmithsdk.LiveActivityStartInput{
Title: "Nightly database backup",
Subtitle: "create snapshot",
NumberOfSteps: 3,
CurrentStep: 1,
Type: "segmented_progress",
Color: "yellow",
}
start, err := activitysmith.LiveActivities.Start(startInput)
if err != nil {
log.Fatal(err)
}
activityID := start.GetActivityId()
```
#### Update
```go theme={null}
updateInput := activitysmithsdk.LiveActivityUpdateInput{
ActivityID: activityID,
Title: "Nightly database backup",
Subtitle: "upload archive",
NumberOfSteps: 3,
CurrentStep: 2,
}
_, err := activitysmith.LiveActivities.Update(updateInput)
if err != nil {
log.Fatal(err)
}
```
#### End
```go theme={null}
endInput := activitysmithsdk.LiveActivityEndInput{
ActivityID: activityID,
Title: "Nightly database backup",
Subtitle: "verify restore",
NumberOfSteps: 3,
CurrentStep: 3,
AutoDismissMinutes: 2,
}
_, err := activitysmith.LiveActivities.End(endInput)
if err != nil {
log.Fatal(err)
}
```
### Progress Type
Use `progress` when the state is naturally continuous. It fits charging,
downloads, sync jobs, uploads, timers, and any flow where a percentage or
numeric range is the clearest signal.
#### Start
```go theme={null}
startInput := activitysmithsdk.LiveActivityStartInput{
Title: "EV Charging",
Subtitle: "Added 30 mi range",
Type: "progress",
Percentage: 15,
}
start, err := activitysmith.LiveActivities.Start(startInput)
if err != nil {
log.Fatal(err)
}
activityID := start.GetActivityId()
```
#### Update
```go theme={null}
updateInput := activitysmithsdk.LiveActivityUpdateInput{
ActivityID: activityID,
Title: "EV Charging",
Subtitle: "Added 120 mi range",
Percentage: 60,
}
_, err := activitysmith.LiveActivities.Update(updateInput)
if err != nil {
log.Fatal(err)
}
```
#### End
```go theme={null}
endInput := activitysmithsdk.LiveActivityEndInput{
ActivityID: activityID,
Title: "EV Charging",
Subtitle: "Added 200 mi range",
Percentage: 100,
AutoDismissMinutes: 2,
}
_, err := activitysmith.LiveActivities.End(endInput)
if err != nil {
log.Fatal(err)
}
```
### Live Activity Action
Just like Actionable Push Notifications, Live Activities can have a button that opens provided URL in a browser or triggers a webhook. Webhooks are executed by the ActivitySmith backend.
#### Open URL action
```go Go theme={null}
startInput := activitysmithsdk.LiveActivityStartInput{
Title: "Deploying payments-api",
Subtitle: "Running database migrations",
NumberOfSteps: 5,
CurrentStep: 3,
Type: "segmented_progress",
Action: &activitysmithsdk.LiveActivityActionInput{
Title: "Open Workflow",
Type: "open_url",
URL: "https://github.com/acme/payments-api/actions/runs/1234567890",
},
}
start, err := activitysmith.LiveActivities.Start(startInput)
if err != nil {
log.Fatal(err)
}
activityID := start.GetActivityId()
```
#### Webhook action
```go Go theme={null}
updateInput := activitysmithsdk.LiveActivityUpdateInput{
ActivityID: activityID,
Title: "Reindexing product search",
Subtitle: "Shard 7 of 12",
NumberOfSteps: 12,
CurrentStep: 7,
Action: &activitysmithsdk.LiveActivityActionInput{
Title: "Pause Reindex",
Type: "webhook",
URL: "https://ops.example.com/hooks/search/reindex/pause",
Method: "POST",
Body: map[string]interface{}{
"job_id": "reindex-2026-03-19",
"requested_by": "activitysmith-go",
},
},
}
_, err = activitysmith.LiveActivities.Update(updateInput)
if err != nil {
log.Fatal(err)
}
```
## Channels
Target specific channels when sending a push or starting a Live Activity.
```go Go theme={null}
pushInput := activitysmithsdk.PushNotificationInput{
Title: "New subscription 💸",
Message: "Customer upgraded to Pro plan",
Channels: []string{"ios-builds", "engineering"},
}
_, err = activitysmith.Notifications.Send(pushInput)
if err != nil {
log.Fatal(err)
}
startInput := activitysmithsdk.LiveActivityStartInput{
Title: "Nightly database backup",
NumberOfSteps: 3,
CurrentStep: 1,
Type: "segmented_progress",
Channels: []string{"ios-builds"},
}
_, err = activitysmith.LiveActivities.Start(startInput)
if err != nil {
log.Fatal(err)
}
```
## Error Handling
SDK calls return `response, err`, so check `err` after every call.
## Additional Resources
View the Go SDK source on GitHub
# Node.js
Source: https://activitysmith.com/docs/sdks/node
Use the ActivitySmith Node.js SDK to send push notifications and Live Activity progress updates from Node services, jobs, and CI workflows.
## Installation
Install the ActivitySmith Node.js SDK with npm:
```js Node theme={null}
npm install activitysmith
```
## Usage
1. [Create an API key](https://activitysmith.com/app/keys)
2. Set `ACTIVITYSMITH_API_KEY` or pass `apiKey` when creating the client.
3. Reuse the client anywhere you send pushes or Live Activity updates.
Create the client once:
```js Node theme={null}
import ActivitySmith from "activitysmith";
const activitysmith = new ActivitySmith({
apiKey: process.env.ACTIVITYSMITH_API_KEY,
});
```
### Send a Push Notification
Use `activitysmith.notifications.send` when a deploy finishes, a customer upgrades, or a background job needs attention. `title` is required. `message` and `subtitle` are optional.
```js Node theme={null}
await activitysmith.notifications.send({
title: "New subscription 💸",
message: "Customer upgraded to Pro plan",
});
```
### Rich Push Notifications with Media
```js Node theme={null}
await activitysmith.notifications.send({
title: "Homepage ready",
message: "Your agent finished the redesign.",
media: "https://cdn.example.com/output/homepage-v2.png",
redirection: "https://github.com/acme/web/pull/482",
});
```
Send images, videos, or audio with your push notifications, press and hold to preview media directly from the notification, then tap through to open the linked content.
What will work:
* direct image URL: `.jpg`, `.png`, `.gif`, etc.
* direct audio file URL: `.mp3`, `.m4a`, etc.
* direct video file URL: `.mp4`, `.mov`, etc.
* URL that responds with a proper media `Content-Type`, even if the path has no extension
`media` can be combined with `redirection`, but not with `actions`.
### Actionable Push Notifications
Push notification redirection and actions are optional and can be used to redirect the user to a specific URL when they tap the notification or to trigger a specific action when they long-press the notification. Webhooks are executed by ActivitySmith backend.
```js Node theme={null}
await activitysmith.notifications.send({
title: "New subscription 💸",
message: "Customer upgraded to Pro plan",
redirection: "https://crm.example.com/customers/cus_9f3a1d",
actions: [
{
title: "Open CRM Profile",
type: "open_url",
url: "https://crm.example.com/customers/cus_9f3a1d",
},
{
title: "Start Onboarding Workflow",
type: "webhook",
url: "https://hooks.example.com/activitysmith/onboarding/start",
method: "POST",
body: {
customer_id: "cus_9f3a1d",
plan: "pro",
},
},
],
});
```
## Live Activities
There are three types of Live Activities:
* `metrics`: best for live operational stats like server CPU and memory, queue depth, or replica lag
* `segmented_progress`: best for step-based workflows like deployments, backups, and ETL pipelines
* `progress`: best for continuous jobs like uploads, reindexes, and long-running migrations tracked as a percentage
When working with Live Activities via our API, you have two approaches tailored to different needs. First, the stateless mode is the simplest path - one API call can initiate or update an activity, and another ends it - no state tracking on your side.
This is ideal if you want minimal complexity, perfect for automated workflows like cron jobs.
In contrast, if you need precise lifecycle control, the classic approach offers distinct calls for start, updates, and end, giving you full control over the activity's state.
In the following sections, we'll break down how to implement each method so you can choose what fits your use case best.
### Simple: Let ActivitySmith manage the Live Activity for you
Use a stable `streamKey` to identify the system or workflow you are tracking,
such as a server, deployment, or build pipeline.
This is especially useful for cron jobs and other scheduled tasks where you do
not want to store `activity_id` between runs.
#### Metrics
```ts theme={null}
const status = await activitysmith.liveActivities.stream("prod-web-1", {
content_state: {
title: "Server Health",
subtitle: "prod-web-1",
type: "metrics",
metrics: [
{ label: "CPU", value: 9, unit: "%" },
{ label: "MEM", value: 45, unit: "%" },
],
},
});
```
#### Segmented progress
```ts theme={null}
await activitysmith.liveActivities.stream("nightly-backup", {
content_state: {
title: "Nightly Backup",
subtitle: "upload archive",
type: "segmented_progress",
number_of_steps: 3,
current_step: 2,
},
});
```
#### Progress
```ts theme={null}
await activitysmith.liveActivities.stream("search-reindex", {
content_state: {
title: "Search Reindex",
subtitle: "catalog-v2",
type: "progress",
percentage: 42,
},
});
```
Call `stream(...)` again with the same `streamKey` whenever the state changes.
#### End a stream
Use this when the tracked process is finished and you no longer want the Live
Activity on devices. `content_state` is optional here; include it if you want
to end the stream with a final state.
```ts theme={null}
await activitysmith.liveActivities.endStream("prod-web-1", {
content_state: {
title: "Server Health",
subtitle: "prod-web-1",
type: "metrics",
metrics: [
{ label: "CPU", value: 7, unit: "%" },
{ label: "MEM", value: 38, unit: "%" },
],
},
});
```
If you later send another `stream(...)` request with the same `streamKey`,
ActivitySmith starts a new Live Activity for that stream again.
Stream responses include an `operation` field:
* `started`: ActivitySmith started a new Live Activity for this `streamKey`
* `updated`: ActivitySmith updated the current Live Activity
* `rotated`: ActivitySmith ended the previous Live Activity and started a new one
* `noop`: the incoming state matched the current state, so no update was sent
* `paused`: the stream is paused, so no Live Activity was started or updated
* `ended`: returned by `endStream(...)` after the stream is ended
### Advanced: Full lifecycle control
Use these methods when you want to manage the Live Activity lifecycle yourself:
1. Call `activitysmith.liveActivities.start(...)`.
2. Save the returned `activity_id`.
3. Call `activitysmith.liveActivities.update(...)` as progress changes.
4. Call `activitysmith.liveActivities.end(...)` when the work is finished.
### Metrics Type
Use `metrics` when you want to keep a small set of live stats visible, such as
server health, queue pressure, or database load.
#### Start
```ts theme={null}
const start = await activitysmith.liveActivities.start({
content_state: {
title: "Server Health",
subtitle: "prod-web-1",
type: "metrics",
metrics: [
{ label: "CPU", value: 9, unit: "%" },
{ label: "MEM", value: 45, unit: "%" },
],
},
});
const activityId = start.activity_id;
```
#### Update
```ts theme={null}
await activitysmith.liveActivities.update({
activity_id: activityId,
content_state: {
title: "Server Health",
subtitle: "prod-web-1",
type: "metrics",
metrics: [
{ label: "CPU", value: 76, unit: "%" },
{ label: "MEM", value: 52, unit: "%" },
],
},
});
```
#### End
```ts theme={null}
await activitysmith.liveActivities.end({
activity_id: activityId,
content_state: {
title: "Server Health",
subtitle: "prod-web-1",
type: "metrics",
metrics: [
{ label: "CPU", value: 7, unit: "%" },
{ label: "MEM", value: 38, unit: "%" },
],
auto_dismiss_minutes: 2,
},
});
```
### Segmented Progress Type
Use `segmented_progress` for jobs and workflows that move through clear steps or
phases. It fits jobs like backups, deployments, ETL pipelines, and checklists.
`number_of_steps` is dynamic, so you can increase or decrease it later if the
workflow changes.
#### Start
```ts theme={null}
const start = await activitysmith.liveActivities.start({
content_state: {
title: "Nightly database backup",
subtitle: "create snapshot",
number_of_steps: 3,
current_step: 1,
type: "segmented_progress",
color: "yellow",
},
});
const activityId = start.activity_id;
```
#### Update
```ts theme={null}
await activitysmith.liveActivities.update({
activity_id: activityId,
content_state: {
title: "Nightly database backup",
subtitle: "upload archive",
number_of_steps: 3,
current_step: 2,
},
});
```
#### End
```ts theme={null}
await activitysmith.liveActivities.end({
activity_id: activityId,
content_state: {
title: "Nightly database backup",
subtitle: "verify restore",
number_of_steps: 3,
current_step: 3,
auto_dismiss_minutes: 2,
},
});
```
### Progress Type
Use `progress` when the state is naturally continuous. It fits charging,
downloads, sync jobs, uploads, timers, and any flow where a percentage or
numeric range is the clearest signal.
#### Start
```ts theme={null}
const start = await activitysmith.liveActivities.start({
content_state: {
title: "EV Charging",
subtitle: "Added 30 mi range",
type: "progress",
percentage: 15,
color: "lime",
},
});
const activityId = start.activity_id;
```
#### Update
```ts theme={null}
await activitysmith.liveActivities.update({
activity_id: activityId,
content_state: {
title: "EV Charging",
subtitle: "Added 120 mi range",
percentage: 60,
},
});
```
#### End
```ts theme={null}
await activitysmith.liveActivities.end({
activity_id: activityId,
content_state: {
title: "EV Charging",
subtitle: "Added 200 mi range",
percentage: 100,
auto_dismiss_minutes: 2,
},
});
```
### Live Activity Action
Just like Actionable Push Notifications, Live Activities can have a button that opens provided URL in a browser or triggers a webhook. Webhooks are executed by the ActivitySmith backend.
#### Open URL action
```js Node theme={null}
const start = await activitysmith.liveActivities.start({
content_state: {
title: "Deploying payments-api",
subtitle: "Running database migrations",
number_of_steps: 5,
current_step: 3,
type: "segmented_progress",
},
action: {
title: "Open Workflow",
type: "open_url",
url: "https://github.com/acme/payments-api/actions/runs/1234567890",
},
});
const activityId = start.activity_id;
```
#### Webhook action
```js Node theme={null}
await activitysmith.liveActivities.update({
activity_id: activityId,
content_state: {
title: "Reindexing product search",
subtitle: "Shard 7 of 12",
number_of_steps: 12,
current_step: 7,
},
action: {
title: "Pause Reindex",
type: "webhook",
url: "https://ops.example.com/hooks/search/reindex/pause",
method: "POST",
body: {
job_id: "reindex-2026-03-19",
requested_by: "activitysmith-node",
},
},
});
```
## Channels
Target specific channels when sending a push or starting a Live Activity.
```js Node theme={null}
await activitysmith.notifications.send({
title: "New subscription 💸",
message: "Customer upgraded to Pro plan",
channels: ["ios-builds", "engineering"],
});
await activitysmith.liveActivities.start({
channels: ["ios-builds"],
content_state: {
title: "Nightly database backup",
number_of_steps: 3,
current_step: 1,
type: "segmented_progress",
},
});
```
## Error Handling
SDK calls return promises, so wrap API calls with `try/catch`:
```js Node theme={null}
try {
await activitysmith.notifications.send({ title: "Hello" });
} catch (error) {
console.error(error);
}
```
## Additional Resources
Install the ActivitySmith Node.js SDK from npm
View the Node.js SDK source on GitHub
# Overview
Source: https://activitysmith.com/docs/sdks/overview
ActivitySmith SDKs and the CLI help you send push notifications and Live Activities to your iOS devices.
## Official SDKs
Explore the Node SDK for ActivitySmith.
Explore the Python SDK for ActivitySmith.
Explore the Go SDK for ActivitySmith.
Explore the PHP SDK for ActivitySmith.
Explore the Ruby SDK for ActivitySmith.
Use ActivitySmith directly from the command line.
# PHP
Source: https://activitysmith.com/docs/sdks/php
Use the ActivitySmith PHP SDK to send push notifications and Live Activity progress updates from PHP apps, queues, and scheduled jobs.
## Installation
Install the ActivitySmith PHP SDK with Composer:
```php PHP theme={null}
composer require activitysmith/activitysmith
```
## Usage
1. [Create an API key](https://activitysmith.com/app/keys)
2. Set `ACTIVITYSMITH_API_KEY` or pass it directly to `ActivitySmith`.
3. Reuse the client anywhere you send pushes or Live Activity updates.
Create the client once:
```php PHP theme={null}
notifications->send` when a deploy finishes, a customer upgrades, or a background job needs attention. `title` is required. `message` and `subtitle` are optional.
```php PHP theme={null}
$activitysmith->notifications->send([
'title' => 'New subscription 💸',
'message' => 'Customer upgraded to Pro plan',
]);
```
### Rich Push Notifications with Media
```php PHP theme={null}
$activitysmith->notifications->send([
'title' => 'Homepage ready',
'message' => 'Your agent finished the redesign.',
'media' => 'https://cdn.example.com/output/homepage-v2.png',
'redirection' => 'https://github.com/acme/web/pull/482',
]);
```
Send images, videos, or audio with your push notifications, press and hold to preview media directly from the notification, then tap through to open the linked content.
What will work:
* direct image URL: `.jpg`, `.png`, `.gif`, etc.
* direct audio file URL: `.mp3`, `.m4a`, etc.
* direct video file URL: `.mp4`, `.mov`, etc.
* URL that responds with a proper media `Content-Type`, even if the path has no extension
`media` can be combined with `redirection`, but not with `actions`.
### Actionable Push Notifications
Push notification redirection and actions are optional and can be used to redirect the user to a specific URL when they tap the notification or to trigger a specific action when they long-press the notification. Webhooks are executed by ActivitySmith backend.
```php PHP theme={null}
$activitysmith->notifications->send([
'title' => 'New subscription 💸',
'message' => 'Customer upgraded to Pro plan',
'redirection' => 'https://crm.example.com/customers/cus_9f3a1d',
'actions' => [
[
'title' => 'Open CRM Profile',
'type' => 'open_url',
'url' => 'https://crm.example.com/customers/cus_9f3a1d',
],
[
'title' => 'Start Onboarding Workflow',
'type' => 'webhook',
'url' => 'https://hooks.example.com/activitysmith/onboarding/start',
'method' => 'POST',
'body' => [
'customer_id' => 'cus_9f3a1d',
'plan' => 'pro',
],
],
],
]);
```
## Live Activities
There are three types of Live Activities:
* `metrics`: best for live operational stats like server CPU and memory, queue depth, or replica lag
* `segmented_progress`: best for step-based workflows like deployments, backups, and ETL pipelines
* `progress`: best for continuous jobs like uploads, reindexes, and long-running migrations tracked as a percentage
When working with Live Activities via our API, you have two approaches tailored
to different needs. First, the stateless mode is the simplest path - one API
call can initiate or update an activity, and another ends it - no state
tracking on your side.
This is ideal if you want minimal complexity, perfect for automated workflows
like cron jobs.
In contrast, if you need precise lifecycle control, the classic approach offers
distinct calls for start, updates, and end, giving you full control over the
activity's state.
In the following sections, we'll break down how to implement each method so you
can choose what fits your use case best.
### Simple: Let ActivitySmith manage the Live Activity for you
Use a stable `streamKey` to identify the system or workflow you are tracking,
such as a server, deployment, build pipeline, cron job, or charging session.
This is especially useful for cron jobs and other scheduled tasks where you do
not want to store `activityId` between runs.
#### Metrics
```php theme={null}
$status = $activitysmith->liveActivities->stream('prod-web-1', [
'content_state' => [
'title' => 'Server Health',
'subtitle' => 'prod-web-1',
'type' => 'metrics',
'metrics' => [
['label' => 'CPU', 'value' => 9, 'unit' => '%'],
['label' => 'MEM', 'value' => 45, 'unit' => '%'],
],
],
]);
```
#### Segmented progress
```php theme={null}
$activitysmith->liveActivities->stream('nightly-backup', [
'content_state' => [
'title' => 'Nightly Backup',
'subtitle' => 'upload archive',
'type' => 'segmented_progress',
'number_of_steps' => 3,
'current_step' => 2,
],
]);
```
#### Progress
```php theme={null}
$activitysmith->liveActivities->stream('search-reindex', [
'content_state' => [
'title' => 'Search Reindex',
'subtitle' => 'catalog-v2',
'type' => 'progress',
'percentage' => 42,
],
]);
```
Call `stream(...)` again with the same `streamKey` whenever the state changes.
#### End a stream
Use this when the tracked process is finished and you no longer want the Live
Activity on devices. `content_state` is optional here; include it if you want
to end the stream with a final state.
```php theme={null}
$activitysmith->liveActivities->endStream('prod-web-1', [
'content_state' => [
'title' => 'Server Health',
'subtitle' => 'prod-web-1',
'type' => 'metrics',
'metrics' => [
['label' => 'CPU', 'value' => 7, 'unit' => '%'],
['label' => 'MEM', 'value' => 38, 'unit' => '%'],
],
],
]);
```
If you later send another `stream(...)` request with the same `streamKey`,
ActivitySmith starts a new Live Activity for that stream again.
Stream responses include an `operation` field:
* `started`: ActivitySmith started a new Live Activity for this `streamKey`
* `updated`: ActivitySmith updated the current Live Activity
* `rotated`: ActivitySmith ended the previous Live Activity and started a new one
* `noop`: the incoming state matched the current state, so no update was sent
* `paused`: the stream is paused, so no Live Activity was started or updated
* `ended`: returned by `endStream(...)` after the stream is ended
### Advanced: Full lifecycle control
Use these methods when you want to manage the Live Activity lifecycle yourself:
1. Call `$activitysmith->liveActivities->start(...)`.
2. Save the returned `activityId`.
3. Call `$activitysmith->liveActivities->update(...)` as progress changes.
4. Call `$activitysmith->liveActivities->end(...)` when the work is finished.
### Metrics Type
Use `metrics` when you want to keep a small set of live stats visible, such as
server health, queue pressure, or database load.
#### Start
```php theme={null}
$start = $activitysmith->liveActivities->start([
'content_state' => [
'title' => 'Server Health',
'subtitle' => 'prod-web-1',
'type' => 'metrics',
'metrics' => [
['label' => 'CPU', 'value' => 9, 'unit' => '%'],
['label' => 'MEM', 'value' => 45, 'unit' => '%'],
],
],
]);
$activityId = $start->getActivityId();
```
#### Update
```php theme={null}
$activitysmith->liveActivities->update([
'activity_id' => $activityId,
'content_state' => [
'title' => 'Server Health',
'subtitle' => 'prod-web-1',
'type' => 'metrics',
'metrics' => [
['label' => 'CPU', 'value' => 76, 'unit' => '%'],
['label' => 'MEM', 'value' => 52, 'unit' => '%'],
],
],
]);
```
#### End
```php theme={null}
$activitysmith->liveActivities->end([
'activity_id' => $activityId,
'content_state' => [
'title' => 'Server Health',
'subtitle' => 'prod-web-1',
'type' => 'metrics',
'metrics' => [
['label' => 'CPU', 'value' => 7, 'unit' => '%'],
['label' => 'MEM', 'value' => 38, 'unit' => '%'],
],
'auto_dismiss_minutes' => 2,
],
]);
```
### Segmented Progress Type
Use `segmented_progress` for jobs and workflows that move through clear steps or
phases. It fits jobs like backups, deployments, ETL pipelines, and checklists.
`number_of_steps` is dynamic, so you can increase or decrease it later if the
workflow changes.
#### Start
```php theme={null}
$start = $activitysmith->liveActivities->start([
'content_state' => [
'title' => 'Nightly database backup',
'subtitle' => 'create snapshot',
'number_of_steps' => 3,
'current_step' => 1,
'type' => 'segmented_progress',
'color' => 'yellow',
],
]);
$activityId = $start->getActivityId();
```
#### Update
```php theme={null}
$activitysmith->liveActivities->update([
'activity_id' => $activityId,
'content_state' => [
'title' => 'Nightly database backup',
'subtitle' => 'upload archive',
'number_of_steps' => 3,
'current_step' => 2,
],
]);
```
#### End
```php theme={null}
$activitysmith->liveActivities->end([
'activity_id' => $activityId,
'content_state' => [
'title' => 'Nightly database backup',
'subtitle' => 'verify restore',
'number_of_steps' => 3,
'current_step' => 3,
'auto_dismiss_minutes' => 2,
],
]);
```
### Progress Type
Use `progress` when the state is naturally continuous. It fits charging,
downloads, sync jobs, uploads, timers, and any flow where a percentage or
numeric range is the clearest signal.
#### Start
```php theme={null}
$start = $activitysmith->liveActivities->start([
'content_state' => [
'title' => 'EV Charging',
'subtitle' => 'Added 30 mi range',
'type' => 'progress',
'percentage' => 15,
],
]);
$activityId = $start->getActivityId();
```
#### Update
```php theme={null}
$activitysmith->liveActivities->update([
'activity_id' => $activityId,
'content_state' => [
'title' => 'EV Charging',
'subtitle' => 'Added 120 mi range',
'percentage' => 60,
],
]);
```
#### End
```php theme={null}
$activitysmith->liveActivities->end([
'activity_id' => $activityId,
'content_state' => [
'title' => 'EV Charging',
'subtitle' => 'Added 200 mi range',
'percentage' => 100,
'auto_dismiss_minutes' => 2,
],
]);
```
### Live Activity Action
Just like Actionable Push Notifications, Live Activities can have a button that opens provided URL in a browser or triggers a webhook. Webhooks are executed by the ActivitySmith backend.
#### Open URL action
```php PHP theme={null}
$start = $activitysmith->liveActivities->start([
'content_state' => [
'title' => 'Deploying payments-api',
'subtitle' => 'Running database migrations',
'number_of_steps' => 5,
'current_step' => 3,
'type' => 'segmented_progress',
],
'action' => [
'title' => 'Open Workflow',
'type' => 'open_url',
'url' => 'https://github.com/acme/payments-api/actions/runs/1234567890',
],
]);
$activityId = $start->getActivityId();
```
#### Webhook action
```php PHP theme={null}
$activitysmith->liveActivities->update([
'activity_id' => $activityId,
'content_state' => [
'title' => 'Reindexing product search',
'subtitle' => 'Shard 7 of 12',
'number_of_steps' => 12,
'current_step' => 7,
],
'action' => [
'title' => 'Pause Reindex',
'type' => 'webhook',
'url' => 'https://ops.example.com/hooks/search/reindex/pause',
'method' => 'POST',
'body' => [
'job_id' => 'reindex-2026-03-19',
'requested_by' => 'activitysmith-php',
],
],
]);
```
## Channels
Target specific channels when sending a push or starting a Live Activity.
```php PHP theme={null}
$activitysmith->notifications->send([
'title' => 'New subscription 💸',
'message' => 'Customer upgraded to Pro plan',
'channels' => ['ios-builds', 'engineering'],
]);
$activitysmith->liveActivities->start([
'channels' => ['ios-builds'],
'content_state' => [
'title' => 'Nightly database backup',
'number_of_steps' => 3,
'current_step' => 1,
'type' => 'segmented_progress',
],
]);
```
## Error Handling
Wrap SDK calls with `try/catch`:
```php PHP theme={null}
try {
$activitysmith->notifications->send(['title' => 'Hello']);
} catch (\Throwable $error) {
echo $error->getMessage();
}
```
## Additional Resources
Install the ActivitySmith PHP SDK from Packagist
View the PHP SDK source on GitHub
# Python
Source: https://activitysmith.com/docs/sdks/python
Use the ActivitySmith Python SDK to send push notifications and Live Activity progress updates from Python scripts, jobs, and backend services.
## Installation
Install the ActivitySmith Python SDK with pip:
```python Python theme={null}
pip install activitysmith
```
## Usage
1. [Create an API key](https://activitysmith.com/app/keys)
2. Set `ACTIVITYSMITH_API_KEY` or pass it directly to `ActivitySmith`.
3. Reuse the client anywhere you send pushes or Live Activity updates.
Create the client once:
```python Python theme={null}
import os
from activitysmith import ActivitySmith
activitysmith = ActivitySmith(api_key=os.environ.get("ACTIVITYSMITH_API_KEY", "YOUR-API-KEY"))
```
### Send a Push Notification
Use `activitysmith.notifications.send` when a deploy finishes, a customer upgrades, or a background job needs attention. `title` is required. `message` and `subtitle` are optional.
```python Python theme={null}
activitysmith.notifications.send(
{
"title": "New subscription 💸",
"message": "Customer upgraded to Pro plan",
}
)
```
### Rich Push Notifications with Media
```python Python theme={null}
activitysmith.notifications.send(
{
"title": "Homepage ready",
"message": "Your agent finished the redesign.",
"media": "https://cdn.example.com/output/homepage-v2.png",
"redirection": "https://github.com/acme/web/pull/482",
}
)
```
Send images, videos, or audio with your push notifications, press and hold to preview media directly from the notification, then tap through to open the linked content.
What will work:
* direct image URL: `.jpg`, `.png`, `.gif`, etc.
* direct audio file URL: `.mp3`, `.m4a`, etc.
* direct video file URL: `.mp4`, `.mov`, etc.
* URL that responds with a proper media `Content-Type`, even if the path has no extension
`media` can be combined with `redirection`, but not with `actions`.
### Actionable Push Notifications
Push notification redirection and actions are optional and can be used to redirect the user to a specific URL when they tap the notification or to trigger a specific action when they long-press the notification. Webhooks are executed by ActivitySmith backend.
```python Python theme={null}
activitysmith.notifications.send(
{
"title": "New subscription 💸",
"message": "Customer upgraded to Pro plan",
"redirection": "https://crm.example.com/customers/cus_9f3a1d",
"actions": [
{
"title": "Open CRM Profile",
"type": "open_url",
"url": "https://crm.example.com/customers/cus_9f3a1d",
},
{
"title": "Start Onboarding Workflow",
"type": "webhook",
"url": "https://hooks.example.com/activitysmith/onboarding/start",
"method": "POST",
"body": {
"customer_id": "cus_9f3a1d",
"plan": "pro",
},
},
],
}
)
```
## Live Activities
There are three types of Live Activities:
* `metrics`: best for live operational stats like server CPU and memory, queue depth, or replica lag
* `segmented_progress`: best for step-based workflows like deployments, backups, and ETL pipelines
* `progress`: best for continuous jobs like uploads, reindexes, and long-running migrations tracked as a percentage
When working with Live Activities via our API, you have two approaches tailored
to different needs. First, the stateless mode is the simplest path - one API
call can initiate or update an activity, and another ends it - no state
tracking on your side.
This is ideal if you want minimal complexity, perfect for automated workflows
like cron jobs.
In contrast, if you need precise lifecycle control, the classic approach offers
distinct calls for start, updates, and end, giving you full control over the
activity's state.
In the following sections, we'll break down how to implement each method so you
can choose what fits your use case best.
### Simple: Let ActivitySmith manage the Live Activity for you
Use a stable `stream_key` to identify the system or workflow you are tracking,
such as a server, deployment, build pipeline, cron job, or charging session.
This is especially useful for cron jobs and other scheduled tasks where you do
not want to store `activity_id` between runs.
#### Metrics
```python theme={null}
status = activitysmith.live_activities.stream(
"prod-web-1",
{
"content_state": {
"title": "Server Health",
"subtitle": "prod-web-1",
"type": "metrics",
"metrics": [
{"label": "CPU", "value": 9, "unit": "%"},
{"label": "MEM", "value": 45, "unit": "%"},
],
},
},
)
```
#### Segmented progress
```python theme={null}
activitysmith.live_activities.stream(
"nightly-backup",
{
"content_state": {
"title": "Nightly Backup",
"subtitle": "upload archive",
"type": "segmented_progress",
"number_of_steps": 3,
"current_step": 2,
},
},
)
```
#### Progress
```python theme={null}
activitysmith.live_activities.stream(
"search-reindex",
{
"content_state": {
"title": "Search Reindex",
"subtitle": "catalog-v2",
"type": "progress",
"percentage": 42,
},
},
)
```
Call `stream(...)` again with the same `stream_key` whenever the state changes.
#### End a stream
Use this when the tracked process is finished and you no longer want the Live
Activity on devices. `content_state` is optional here; include it if you want
to end the stream with a final state.
```python theme={null}
activitysmith.live_activities.end_stream(
"prod-web-1",
{
"content_state": {
"title": "Server Health",
"subtitle": "prod-web-1",
"type": "metrics",
"metrics": [
{"label": "CPU", "value": 7, "unit": "%"},
{"label": "MEM", "value": 38, "unit": "%"},
],
},
},
)
```
If you later send another `stream(...)` request with the same `stream_key`,
ActivitySmith starts a new Live Activity for that stream again.
Stream responses include an `operation` field:
* `started`: ActivitySmith started a new Live Activity for this `stream_key`
* `updated`: ActivitySmith updated the current Live Activity
* `rotated`: ActivitySmith ended the previous Live Activity and started a new one
* `noop`: the incoming state matched the current state, so no update was sent
* `paused`: the stream is paused, so no Live Activity was started or updated
* `ended`: returned by `end_stream(...)` after the stream is ended
### Advanced: Full lifecycle control
Use these methods when you want to manage the Live Activity lifecycle yourself:
1. Call `activitysmith.live_activities.start(...)`.
2. Save the returned `activity_id`.
3. Call `activitysmith.live_activities.update(...)` as progress changes.
4. Call `activitysmith.live_activities.end(...)` when the work is finished.
### Metrics Type
Use `metrics` when you want to keep a small set of live stats visible, such as
server health, queue pressure, or database load.
#### Start
```python theme={null}
start = activitysmith.live_activities.start(
{
"content_state": {
"title": "Server Health",
"subtitle": "prod-web-1",
"type": "metrics",
"metrics": [
{"label": "CPU", "value": 9, "unit": "%"},
{"label": "MEM", "value": 45, "unit": "%"},
],
},
}
)
activity_id = start.activity_id
```
#### Update
```python theme={null}
activitysmith.live_activities.update(
{
"activity_id": activity_id,
"content_state": {
"title": "Server Health",
"subtitle": "prod-web-1",
"type": "metrics",
"metrics": [
{"label": "CPU", "value": 76, "unit": "%"},
{"label": "MEM", "value": 52, "unit": "%"},
],
},
}
)
```
#### End
```python theme={null}
activitysmith.live_activities.end(
{
"activity_id": activity_id,
"content_state": {
"title": "Server Health",
"subtitle": "prod-web-1",
"type": "metrics",
"metrics": [
{"label": "CPU", "value": 7, "unit": "%"},
{"label": "MEM", "value": 38, "unit": "%"},
],
"auto_dismiss_minutes": 2,
},
}
)
```
### Segmented Progress Type
Use `segmented_progress` for jobs and workflows that move through clear steps or
phases. It fits jobs like backups, deployments, ETL pipelines, and checklists.
`number_of_steps` is dynamic, so you can increase or decrease it later if the
workflow changes.
#### Start
```python theme={null}
start = activitysmith.live_activities.start(
{
"content_state": {
"title": "Nightly database backup",
"subtitle": "create snapshot",
"number_of_steps": 3,
"current_step": 1,
"type": "segmented_progress",
"color": "yellow",
},
}
)
activity_id = start.activity_id
```
#### Update
```python theme={null}
activitysmith.live_activities.update(
{
"activity_id": activity_id,
"content_state": {
"title": "Nightly database backup",
"subtitle": "upload archive",
"number_of_steps": 3,
"current_step": 2,
},
}
)
```
#### End
```python theme={null}
activitysmith.live_activities.end(
{
"activity_id": activity_id,
"content_state": {
"title": "Nightly database backup",
"subtitle": "verify restore",
"number_of_steps": 3,
"current_step": 3,
"auto_dismiss_minutes": 2,
},
}
)
```
### Progress Type
Use `progress` when the state is naturally continuous. It fits charging,
downloads, sync jobs, uploads, timers, and any flow where a percentage or
numeric range is the clearest signal.
#### Start
```python theme={null}
start = activitysmith.live_activities.start(
{
"content_state": {
"title": "EV Charging",
"subtitle": "Added 30 mi range",
"type": "progress",
"percentage": 15,
}
}
)
activity_id = start.activity_id
```
#### Update
```python theme={null}
activitysmith.live_activities.update(
{
"activity_id": activity_id,
"content_state": {
"title": "EV Charging",
"subtitle": "Added 120 mi range",
"percentage": 60,
}
}
)
```
#### End
```python theme={null}
activitysmith.live_activities.end(
{
"activity_id": activity_id,
"content_state": {
"title": "EV Charging",
"subtitle": "Added 200 mi range",
"percentage": 100,
"auto_dismiss_minutes": 2,
}
}
)
```
### Live Activity Action
Just like Actionable Push Notifications, Live Activities can have a button that opens provided URL in a browser or triggers a webhook. Webhooks are executed by the ActivitySmith backend.
#### Open URL action
```python Python theme={null}
start = activitysmith.live_activities.start(
{
"content_state": {
"title": "Deploying payments-api",
"subtitle": "Running database migrations",
"number_of_steps": 5,
"current_step": 3,
"type": "segmented_progress",
},
"action": {
"title": "Open Workflow",
"type": "open_url",
"url": "https://github.com/acme/payments-api/actions/runs/1234567890",
},
}
)
activity_id = start.activity_id
```
#### Webhook action
```python Python theme={null}
activitysmith.live_activities.update(
{
"activity_id": activity_id,
"content_state": {
"title": "Reindexing product search",
"subtitle": "Shard 7 of 12",
"number_of_steps": 12,
"current_step": 7,
},
"action": {
"title": "Pause Reindex",
"type": "webhook",
"url": "https://ops.example.com/hooks/search/reindex/pause",
"method": "POST",
"body": {
"job_id": "reindex-2026-03-19",
"requested_by": "activitysmith-python",
},
},
}
)
```
## Channels
Target specific channels when sending a push or starting a Live Activity.
```python Python theme={null}
activitysmith.notifications.send(
{
"title": "New subscription 💸",
"message": "Customer upgraded to Pro plan",
"channels": ["ios-builds", "engineering"],
}
)
activitysmith.live_activities.start(
{
"channels": ["ios-builds"],
"content_state": {
"title": "Nightly database backup",
"number_of_steps": 3,
"current_step": 1,
"type": "segmented_progress",
},
}
)
```
## Error Handling
Wrap API calls with `try/except`. The SDK raises exceptions for non-2xx responses. Rate limit errors use the `error` and `message` fields, and Live Activity limit errors include `limit` and `active`. See [Rate Limits](/rate-limits) for details.
## Additional Resources
Install the ActivitySmith Python SDK from PyPI
View the Python SDK source on GitHub
# Ruby
Source: https://activitysmith.com/docs/sdks/ruby
Use the ActivitySmith Ruby SDK to send push notifications and Live Activity progress updates from Ruby apps, workers, and scheduled jobs.
## Installation
Install the ActivitySmith Ruby SDK with RubyGems:
```ruby Ruby theme={null}
gem install activitysmith
```
## Usage
1. [Create an API key](https://activitysmith.com/app/keys)
2. Set `ACTIVITYSMITH_API_KEY` or pass it directly to `ActivitySmith::Client`.
3. Reuse the client anywhere you send pushes or Live Activity updates.
Create the client once:
```ruby Ruby theme={null}
require "activitysmith"
api_key = ENV["ACTIVITYSMITH_API_KEY"] || "YOUR-API-KEY"
activitysmith = ActivitySmith::Client.new(api_key: api_key)
```
### Send a Push Notification
Use `activitysmith.notifications.send` when a deploy finishes, a customer upgrades, or a background job needs attention. `title` is required. `message` and `subtitle` are optional.
```ruby Ruby theme={null}
activitysmith.notifications.send(
{
title: "New subscription 💸",
message: "Customer upgraded to Pro plan"
}
)
```
### Rich Push Notifications with Media
```ruby Ruby theme={null}
activitysmith.notifications.send(
{
title: "Homepage ready",
message: "Your agent finished the redesign.",
media: "https://cdn.example.com/output/homepage-v2.png",
redirection: "https://github.com/acme/web/pull/482"
}
)
```
Send images, videos, or audio with your push notifications, press and hold to preview media directly from the notification, then tap through to open the linked content.
What will work:
* direct image URL: `.jpg`, `.png`, `.gif`, etc.
* direct audio file URL: `.mp3`, `.m4a`, etc.
* direct video file URL: `.mp4`, `.mov`, etc.
* URL that responds with a proper media `Content-Type`, even if the path has no extension
`media` can be combined with `redirection`, but not with `actions`.
### Actionable Push Notifications
Push notification redirection and actions are optional and can be used to redirect the user to a specific URL when they tap the notification or to trigger a specific action when they long-press the notification. Webhooks are executed by ActivitySmith backend.
```ruby Ruby theme={null}
activitysmith.notifications.send(
{
title: "New subscription 💸",
message: "Customer upgraded to Pro plan",
redirection: "https://crm.example.com/customers/cus_9f3a1d",
actions: [
{
title: "Open CRM Profile",
type: "open_url",
url: "https://crm.example.com/customers/cus_9f3a1d"
},
{
title: "Start Onboarding Workflow",
type: "webhook",
url: "https://hooks.example.com/activitysmith/onboarding/start",
method: "POST",
body: {
customer_id: "cus_9f3a1d",
plan: "pro"
}
}
]
}
)
```
## Live Activities
There are three types of Live Activities:
* `metrics`: best for live operational stats like server CPU and memory, queue depth, or replica lag
* `segmented_progress`: best for step-based workflows like deployments, backups, and ETL pipelines
* `progress`: best for continuous jobs like uploads, reindexes, and long-running migrations tracked as a percentage
When working with Live Activities via our API, you have two approaches tailored
to different needs. First, the stateless mode is the simplest path - one API
call can initiate or update an activity, and another ends it - no state
tracking on your side.
This is ideal if you want minimal complexity, perfect for automated workflows
like cron jobs.
In contrast, if you need precise lifecycle control, the classic approach offers
distinct calls for start, updates, and end, giving you full control over the
activity's state.
In the following sections, we'll break down how to implement each method so you
can choose what fits your use case best.
### Simple: Let ActivitySmith manage the Live Activity for you
Use a stable `stream_key` to identify the system or workflow you are tracking,
such as a server, deployment, build pipeline, cron job, or charging session.
This is especially useful for cron jobs and other scheduled tasks where you do
not want to store `activity_id` between runs.
#### Metrics
```ruby theme={null}
status = activitysmith.live_activities.stream(
"prod-web-1",
{
content_state: {
title: "Server Health",
subtitle: "prod-web-1",
type: "metrics",
metrics: [
{ label: "CPU", value: 9, unit: "%" },
{ label: "MEM", value: 45, unit: "%" }
]
}
}
)
```
#### Segmented progress
```ruby theme={null}
activitysmith.live_activities.stream(
"nightly-backup",
{
content_state: {
title: "Nightly Backup",
subtitle: "upload archive",
type: "segmented_progress",
number_of_steps: 3,
current_step: 2
}
}
)
```
#### Progress
```ruby theme={null}
activitysmith.live_activities.stream(
"search-reindex",
{
content_state: {
title: "Search Reindex",
subtitle: "catalog-v2",
type: "progress",
percentage: 42
}
}
)
```
Call `stream(...)` again with the same `stream_key` whenever the state changes.
#### End a stream
Use this when the tracked process is finished and you no longer want the Live
Activity on devices. `content_state` is optional here; include it if you want
to end the stream with a final state.
```ruby theme={null}
activitysmith.live_activities.end_stream(
"prod-web-1",
{
content_state: {
title: "Server Health",
subtitle: "prod-web-1",
type: "metrics",
metrics: [
{ label: "CPU", value: 7, unit: "%" },
{ label: "MEM", value: 38, unit: "%" }
]
}
}
)
```
If you later send another `stream(...)` request with the same `stream_key`,
ActivitySmith starts a new Live Activity for that stream again.
Stream responses include an `operation` field:
* `started`: ActivitySmith started a new Live Activity for this `stream_key`
* `updated`: ActivitySmith updated the current Live Activity
* `rotated`: ActivitySmith ended the previous Live Activity and started a new one
* `noop`: the incoming state matched the current state, so no update was sent
* `paused`: the stream is paused, so no Live Activity was started or updated
* `ended`: returned by `end_stream(...)` after the stream is ended
### Advanced: Full lifecycle control
Use these methods when you want to manage the Live Activity lifecycle yourself:
1. Call `activitysmith.live_activities.start(...)`.
2. Save the returned `activity_id`.
3. Call `activitysmith.live_activities.update(...)` as progress changes.
4. Call `activitysmith.live_activities.end(...)` when the work is finished.
### Metrics Type
Use `metrics` when you want to keep a small set of live stats visible, such as
server health, queue pressure, or database load.
#### Start
```ruby theme={null}
start = activitysmith.live_activities.start(
{
content_state: {
title: "Server Health",
subtitle: "prod-web-1",
type: "metrics",
metrics: [
{ label: "CPU", value: 9, unit: "%" },
{ label: "MEM", value: 45, unit: "%" }
]
}
}
)
activity_id = start.activity_id
```
#### Update
```ruby theme={null}
activitysmith.live_activities.update(
{
activity_id: activity_id,
content_state: {
title: "Server Health",
subtitle: "prod-web-1",
type: "metrics",
metrics: [
{ label: "CPU", value: 76, unit: "%" },
{ label: "MEM", value: 52, unit: "%" }
]
}
}
)
```
#### End
```ruby theme={null}
activitysmith.live_activities.end(
{
activity_id: activity_id,
content_state: {
title: "Server Health",
subtitle: "prod-web-1",
type: "metrics",
metrics: [
{ label: "CPU", value: 7, unit: "%" },
{ label: "MEM", value: 38, unit: "%" }
],
auto_dismiss_minutes: 2
}
}
)
```
### Segmented Progress Type
Use `segmented_progress` for jobs and workflows that move through clear steps or
phases. It fits jobs like backups, deployments, ETL pipelines, and checklists.
`number_of_steps` is dynamic, so you can increase or decrease it later if the
workflow changes.
#### Start
```ruby theme={null}
start = activitysmith.live_activities.start(
{
content_state: {
title: "Nightly database backup",
subtitle: "create snapshot",
number_of_steps: 3,
current_step: 1,
type: "segmented_progress",
color: "yellow"
}
}
)
activity_id = start.activity_id
```
#### Update
```ruby theme={null}
activitysmith.live_activities.update(
{
activity_id: activity_id,
content_state: {
title: "Nightly database backup",
subtitle: "upload archive",
number_of_steps: 3,
current_step: 2
}
}
)
```
#### End
```ruby theme={null}
activitysmith.live_activities.end(
{
activity_id: activity_id,
content_state: {
title: "Nightly database backup",
subtitle: "verify restore",
number_of_steps: 3,
current_step: 3,
auto_dismiss_minutes: 2
}
}
)
```
### Progress Type
Use `progress` when the state is naturally continuous. It fits charging,
downloads, sync jobs, uploads, timers, and any flow where a percentage or
numeric range is the clearest signal.
#### Start
```ruby theme={null}
start = activitysmith.live_activities.start(
{
content_state: {
title: "EV Charging",
subtitle: "Added 30 mi range",
type: "progress",
percentage: 15
}
}
)
activity_id = start.activity_id
```
#### Update
```ruby theme={null}
activitysmith.live_activities.update(
{
activity_id: activity_id,
content_state: {
title: "EV Charging",
subtitle: "Added 120 mi range",
percentage: 60
}
}
)
```
#### End
```ruby theme={null}
activitysmith.live_activities.end(
{
activity_id: activity_id,
content_state: {
title: "EV Charging",
subtitle: "Added 200 mi range",
percentage: 100,
auto_dismiss_minutes: 2
}
}
)
```
### Live Activity Action
Just like Actionable Push Notifications, Live Activities can have a button that opens provided URL in a browser or triggers a webhook. Webhooks are executed by the ActivitySmith backend.
#### Open URL action
```ruby Ruby theme={null}
start = activitysmith.live_activities.start(
{
content_state: {
title: "Deploying payments-api",
subtitle: "Running database migrations",
number_of_steps: 5,
current_step: 3,
type: "segmented_progress"
},
action: {
title: "Open Workflow",
type: "open_url",
url: "https://github.com/acme/payments-api/actions/runs/1234567890"
}
}
)
activity_id = start.activity_id
```
#### Webhook action
```ruby Ruby theme={null}
activitysmith.live_activities.update(
{
activity_id: activity_id,
content_state: {
title: "Reindexing product search",
subtitle: "Shard 7 of 12",
number_of_steps: 12,
current_step: 7
},
action: {
title: "Pause Reindex",
type: "webhook",
url: "https://ops.example.com/hooks/search/reindex/pause",
method: "POST",
body: {
job_id: "reindex-2026-03-19",
requested_by: "activitysmith-ruby"
}
}
}
)
```
## Channels
Target specific channels when sending a push or starting a Live Activity.
```ruby Ruby theme={null}
activitysmith.notifications.send(
{
title: "New subscription 💸",
message: "Customer upgraded to Pro plan",
channels: ["ios-builds", "engineering"]
}
)
activitysmith.live_activities.start(
{
channels: ["ios-builds"],
content_state: {
title: "Nightly database backup",
number_of_steps: 3,
current_step: 1,
type: "segmented_progress"
}
}
)
```
## Error Handling
Handle API errors with `begin/rescue` around SDK calls:
```ruby Ruby theme={null}
begin
activitysmith.notifications.send(
{ title: "Hello" }
)
rescue OpenapiClient::ApiError => err
puts "Request failed: #{err.code} #{err.message}"
end
```
## Additional Resources
Install the ActivitySmith Ruby SDK from RubyGems
View the Ruby SDK source on GitHub
# Team Members
Source: https://activitysmith.com/docs/team
Manage your team from one place: invite members, track pending invites, and remove access.
## Managing Team Members
The **Members** page is where team owners manage access for the whole workspace.
You can:
* Invite one or many people at once
* See active users and their role
* Track pending invitations and expiration dates
* Remove members when they no longer need access
## Roles
ActivitySmith currently supports two team roles:
| Role | Access |
| -------- | ------------------------------------------------------------- |
| `owner` | Full access, including inviting and removing members |
| `member` | Standard workspace access without team management permissions |
## Invite Team Members
Only team owners can invite new members.
1. Open your ActivitySmith dashboard.
2. Go to **Members** in the left sidebar.
3. Click **Invite members**.
4. Enter one or more email addresses in the invite modal.
5. Separate multiple emails with commas or new lines.
6. Click **Send Invites**.
Newly invited users appear in the **Invited users** table with:
* `pending` status until accepted
* Invite date (`Invited`)
* Expiration date (`Expires`)
Members are added to **Active users** only after they accept the invite.
## Remove a Member
Only team owners can remove members.
1. Open **Members**.
2. Find the user in **Active users**.
3. Click **Remove** in the **Actions** column.
4. Confirm the removal.
The removed member loses team access immediately.