Install the ActivitySmith Node.js SDK with npm:
npm install activitysmith- Create an API key
- Set
ACTIVITYSMITH_API_KEYor passapiKeywhen creating the client.
import ActivitySmith from "activitysmith";
const activitysmith = new ActivitySmith({
apiKey: process.env.ACTIVITYSMITH_API_KEY,
});Send an immediate notification for a completed task or event.
await activitysmith.notifications.send({
title: "New subscription 💸",
message: "Customer upgraded to Pro plan",
});await activitysmith.notifications.send({
title: "Homepage ready",
message: "Your agent finished the redesign.",
media: "https://cdn.example.com/output/homepage-v2.png",
});Attach images, videos, or audio to your Push Notifications. Press and hold the notification to preview the media.
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 cannot be combined with actions.
Open a web page, run an iOS Shortcut, or open an app when someone taps the notification. redirection supports:
- HTTP/HTTPS: Web pages, e.g.
https://example.com - Shortcuts: Run Jarvis with
shortcuts://run-shortcut?name=Jarvis - App deep links: Installed apps or specific content within them
- Spotify: A track, e.g.
spotify:track:6rqhFgbbKwnb9MLmUQDhG6 - Termius:
termius://to open the app - Claude:
claude://codeto open the Code tab - ChatGPT:
chatgpt://to open the app
- Spotify: A track, e.g.
await activitysmith.notifications.send({
title: "Homepage ready",
message: "Your agent finished the redesign.",
redirection: "https://github.com/acme/web/pull/482",
});open_url actions open a web page, run an iOS Shortcut, or open an app when someone taps the button. Supported links:
- HTTP/HTTPS: Web pages, e.g.
https://example.com - Shortcuts: Run Jarvis with
shortcuts://run-shortcut?name=Jarvis - App deep links: Installed apps or specific content within them
- Spotify: A track, e.g.
spotify:track:6rqhFgbbKwnb9MLmUQDhG6 - Termius:
termius://to open the app - Claude:
claude://codeto open the Code tab - ChatGPT:
chatgpt://to open the app
- Spotify: A track, e.g.
Webhooks are executed by the ActivitySmith backend and must use HTTPS.
await activitysmith.notifications.send({
title: "New subscription 💸",
message: "Customer upgraded to Pro plan",
actions: [
{
title: "Open CRM",
type: "open_url",
url: "https://crm.example.com/customers/cus_9f3a1d",
},
{
title: "Chat with Jarvis",
type: "open_url",
url: "shortcuts://run-shortcut?name=Jarvis",
},
{
title: "Start Onboarding Workflow",
type: "webhook",
url: "https://hooks.example.com/activitysmith/onboarding/start",
method: "POST",
body: {
customer_id: "cus_9f3a1d",
plan: "pro",
},
},
],
});Choose the Live Activity type that matches what you want to show:
Stats: Show up to 8 labeled values on your Lock Screen, from revenue and orders to uptime and conversion.
Metrics: Track two related values with segmented bars, such as CPU and memory.
Segmented Progress: Show progress through a known set of steps, like build, test, deploy, and verify.
Progress: Show percentage progress for jobs that move continuously toward completion.
Alert: Show status updates with a clear message, badge, and icon. When you add an action button, color controls the button tint.
Timer: Count down from a duration, or count up from 00:00 while a job runs.
Use a stable streamKey to identify the metric, job, deployment, or system you want to keep visible. The first stream(...) call starts the Live Activity. Later calls with the same streamKey update it.
await activitysmith.liveActivities.stream("sales-hourly", {
content_state: {
title: "Sales",
subtitle: "last hour",
type: "stats",
metrics: [
{ label: "Revenue", value: "$2430", color: "blue" },
{ label: "Orders", value: "37", color: "green" },
{ label: "Conversion", value: "4.8%", color: "magenta" },
{ label: "Avg Order", value: "$65.68", color: "yellow" },
{ label: "Refunds", value: "$84", color: "red" },
{ label: "New Buyers", value: "18", color: "cyan" },
],
},
});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: "%" },
],
},
});await activitysmith.liveActivities.stream("nightly-backup", {
content_state: {
title: "Nightly Backup",
subtitle: "upload archive",
type: "segmented_progress",
number_of_steps: 3,
current_step: 2,
},
});await activitysmith.liveActivities.stream("search-reindex", {
content_state: {
title: "Search Reindex",
subtitle: "catalog-v2",
type: "progress",
percentage: 42,
},
});await activitysmith.liveActivities.stream("customer-ops", {
content_state: ActivitySmith.contentState({
title: "Reactivation",
message: "Lumen came back after 2 weeks",
type: "alert",
icon: ActivitySmith.alertIcon("cloud.sun", { color: "yellow" }),
badge: ActivitySmith.alertBadge("Customer", { color: "magenta" }),
}),
});await activitysmith.liveActivities.stream("benchmark-run", {
content_state: {
title: "Benchmark Run",
subtitle: "sampling",
type: "timer",
duration_seconds: 300,
color: "cyan",
},
});For a countdown, send duration_seconds. You can update title, subtitle, color, or any other visible field as the work changes. Leave duration_seconds out unless you want to change the timer.
To start at 00:00 and count up, set counts_down: false and leave out duration_seconds.
Call endStream(...) with the same streamKey to dismiss the Live Activity. You can include final values before it is removed. Set auto_dismiss_seconds to dismiss it after a delay in seconds, or auto_dismiss_minutes for minutes. Use 0 for immediate dismissal. Seconds take precedence if both are set.
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: "%" },
],
auto_dismiss_seconds: 30,
},
});Add more context to Live Activities with icons and badges.
Supported Live Activity types: stats, metrics, progress, segmented_progress, alert, and timer.
await activitysmith.liveActivities.stream("prod-web-1", {
content_state: ActivitySmith.contentState({
title: "Server Health",
subtitle: "prod-web-1",
type: "metrics",
icon: ActivitySmith.alertIcon("server.rack", { color: "blue" }),
metrics: [
{ label: "CPU", value: 18, unit: "%" },
{ label: "MEM", value: 42, unit: "%" },
],
}),
});The icon.symbol value is an Apple SF Symbol name. Browse the catalog with one of these tools:
- ActivitySmith app - Open Settings -> SF Symbols to browse 45 hand-picked icons ready to use
- SF Symbols - Apple's official macOS app
- Interactful - free third-party iOS app listing all SF Symbols under Foundations -> Iconography
Badges are supported by alert, progress, and segmented_progress Live Activities.
await activitysmith.liveActivities.stream("nightly-database-backup", {
content_state: ActivitySmith.contentState({
title: "Nightly Database Backup",
subtitle: "verify restore",
type: "progress",
badge: ActivitySmith.alertBadge("S3", { color: "cyan" }),
percentage: 62,
}),
});Choose from these colors for the Live Activity accent, including progress bars and action buttons, or apply them to an individual icon or badge:
lime, green, cyan, blue, purple, magenta, red, orange, yellow, gray
Live Activities can include an action button.
open_url: Open a web page or run an iOS Shortcutwebhook: Trigger a backend GET/POST workflow
Open a web page or run an iOS Shortcut when someone taps the button. Supported links:
- HTTP/HTTPS: Web pages, e.g.
https://example.com - Shortcuts: Run Jarvis with
shortcuts://run-shortcut?name=Jarvis
await activitysmith.liveActivities.stream("prod-web-1", {
content_state: {
title: "Server Health",
subtitle: "prod-web-1",
type: "metrics",
metrics: [
{ label: "CPU", value: 76, unit: "%" },
{ label: "MEM", value: 52, unit: "%" },
],
},
action: {
title: "Dashboard",
type: "open_url",
url: "https://status.example.com/servers/prod-web-1",
},
});await activitysmith.liveActivities.stream("prod-web-1", {
content_state: {
title: "Server Health",
subtitle: "prod-web-1",
type: "metrics",
metrics: [
{ label: "CPU", value: 76, unit: "%" },
{ label: "MEM", value: 52, unit: "%" },
],
},
action: {
title: "Chat with Jarvis",
type: "open_url",
url: "shortcuts://run-shortcut?name=Jarvis",
},
});await activitysmith.liveActivities.stream("search-reindex", {
content_state: {
title: "Reindexing product search",
subtitle: "Shard 7 of 12",
type: "segmented_progress",
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",
},
},
});Use secondary_action when you want a second button beside the primary action.
The secondary action button is supported for alert, progress, and segmented_progress Live Activities. Both buttons use the same open_url, webhook, and Apple Shortcut payload shapes.
await activitysmith.liveActivities.stream("agent-approval", {
content_state: {
title: "Approval Needed",
message: "Should I send the follow-up email to Brightlane?",
type: "alert",
color: "green",
icon: {
symbol: "sparkles",
color: "green",
},
badge: {
title: "Agent",
color: "green",
},
},
action: {
title: "Send",
type: "webhook",
url: "https://agent.example.com/live-activity/approve",
method: "POST",
body: {
approval_id: "approval_01JY3J7Q9S0P8M1V5PZK7DR4M2",
decision: "send",
},
},
secondary_action: {
title: "Deny",
type: "webhook",
url: "https://agent.example.com/live-activity/deny",
method: "POST",
body: {
approval_id: "approval_01JY3J7Q9S0P8M1V5PZK7DR4M2",
decision: "deny",
},
},
});ActivitySmith lets you display any value on your Lock Screen with widgets - SaaS metrics, revenue, signups, uptime, habits, or anything else you want to track. Create a metric in the web app, then update the metric value using our API, add a widget to your lock screen and it will fetch the latest update automatically.
Use the metric key to update its value.
await activitysmith.metrics.update("deploy.success_rate", 99.9);String metric values work too.
await activitysmith.metrics.update("prod.status", "healthy");Show the number you care about on your ActivitySmith app icon. Track MRR, a customer count, a stock price, or any other value you want to keep in view.
await activitysmith.badgeCount(8333);Pass 0 to clear the badge.
await activitysmith.badgeCount(0);Metadata adds extra information to Push Notification and Live Activity details in ActivitySmith. It does not appear in the notification or Live Activity on your device.
await activitysmith.notifications.send({
title: "New subscription 💸",
message: "Customer upgraded to Pro plan",
metadata: {
customer_id: "382",
plan: "Pro",
amount: 29,
trial: false,
},
});
await activitysmith.liveActivities.stream("customer-import", {
content_state: {
title: "Customer Import",
type: "progress",
percentage: 60,
},
metadata: {
job_id: "import-382",
records: 1200,
},
});Values can be strings, numbers, or booleans. Metadata supports up to 50 entries and 16 KB of JSON, with keys up to 100 characters and strings up to 4,000 characters. Nested objects, arrays, and null values are not supported.
Use tags to organize and filter your Push Notification and Live Activity history. Tags are created automatically when you first use them.
await activitysmith.notifications.send({
title: "New subscription 💸",
message: "Customer upgraded to Pro plan",
tags: ["user:382", "billing"],
});On Live Activity stream updates and legacy update or end calls, omit tags to keep existing Tags, supply a list to replace them, or pass tags: [] to clear them.
await activitysmith.liveActivities.update({
activity_id: "YOUR_ACTIVITY_ID",
content_state: { title: "Customer Import", percentage: 60 },
tags: [],
});Use channels to target specific team members or devices when sending Push Notifications, Live Activities, or App Icon Badge Count updates. Omit it for account-wide delivery.
await activitysmith.notifications.send({
title: "New subscription 💸",
message: "Customer upgraded to Pro plan",
channels: ["sales", "customer-success"],
});
await activitysmith.liveActivities.stream("nightly-backup", {
content_state: {
title: "Nightly database backup",
number_of_steps: 3,
current_step: 1,
type: "segmented_progress",
},
channels: ["ios-builds"],
});
await activitysmith.badgeCount(3, {
channels: ["sales", "customer-success"],
});SDK calls return promises, so you can wrap API calls with try/catch:
try {
await activitysmith.notifications.send({ title: "Hello" });
} catch (error) {
console.error(error);
}Install the ActivitySmith Node.js SDK from npm















