@@ -116,27 +116,230 @@ activitysmith.notifications.send(
116116
117117## Live Activities
118118
119- Live Activities come in two UI types, but the lifecycle stays the same:
120- start the activity, keep the returned ` activity_id ` , update it as state
121- changes, then end it when the work is done.
119+ <p align =" center " >
120+ <img src =" https://cdn.activitysmith.com/features/metrics-live-activity-action.png " alt =" Live Activities example " width =" 680 " />
121+ </p >
122+
123+ ActivitySmith supports two ways to drive Live Activities:
124+
125+ - Recommended: stream updates with ` activitysmith.live_activities.stream(...) `
126+ - Advanced: manual lifecycle control with ` start ` , ` update ` , and ` end `
127+
128+ Use stream updates when you want the easiest, stateless flow. You don't need to
129+ store ` activity_id ` or manage lifecycle state yourself. Send the latest state
130+ for a stable ` stream_key ` and ActivitySmith will start or update the Live
131+ Activity for you. When the tracked process is over, call ` end_stream(...) ` .
132+
133+ Use the manual lifecycle methods when you need direct control over a specific
134+ Live Activity instance.
135+
136+ Live Activity UI types:
137+
138+ - ` metrics ` : best for live operational stats like server CPU and memory, queue depth, or replica lag
139+ - ` segmented_progress ` : best for step-based workflows like deployments, backups, and ETL pipelines
140+ - ` progress ` : best for continuous jobs like uploads, reindexes, and long-running migrations tracked as a percentage
141+
142+ ### Recommended: Stream updates
143+
144+ Use a stable ` stream_key ` to identify the system or workflow you are tracking,
145+ such as a server, deployment, build pipeline, cron job, or charging session.
146+ This is especially useful for cron jobs and other scheduled tasks where you do
147+ not want to store ` activity_id ` between runs.
148+
149+ #### Metrics
150+
151+ <p align =" center " >
152+ <img src =" https://cdn.activitysmith.com/features/metrics-live-activity-start.png " alt =" Metrics stream example " width =" 680 " />
153+ </p >
154+
155+ ``` python
156+ status = activitysmith.live_activities.stream(
157+ " prod-web-1" ,
158+ {
159+ " content_state" : {
160+ " title" : " Server Health" ,
161+ " subtitle" : " prod-web-1" ,
162+ " type" : " metrics" ,
163+ " metrics" : [
164+ {" label" : " CPU" , " value" : 9 , " unit" : " %" },
165+ {" label" : " MEM" , " value" : 45 , " unit" : " %" },
166+ ],
167+ },
168+ },
169+ )
170+ ```
171+
172+ #### Segmented progress
173+
174+ <p align =" center " >
175+ <img src =" https://cdn.activitysmith.com/features/update-live-activity.png " alt =" Segmented progress stream example " width =" 680 " />
176+ </p >
177+
178+ ``` python
179+ activitysmith.live_activities.stream(
180+ " nightly-backup" ,
181+ {
182+ " content_state" : {
183+ " title" : " Nightly Backup" ,
184+ " subtitle" : " upload archive" ,
185+ " type" : " segmented_progress" ,
186+ " number_of_steps" : 3 ,
187+ " current_step" : 2 ,
188+ },
189+ },
190+ )
191+ ```
192+
193+ #### Progress
194+
195+ <p align =" center " >
196+ <img src =" https://cdn.activitysmith.com/features/progress-live-activity.png " alt =" Progress stream example " width =" 680 " />
197+ </p >
198+
199+ ``` python
200+ activitysmith.live_activities.stream(
201+ " search-reindex" ,
202+ {
203+ " content_state" : {
204+ " title" : " Search Reindex" ,
205+ " subtitle" : " catalog-v2" ,
206+ " type" : " progress" ,
207+ " percentage" : 42 ,
208+ },
209+ },
210+ )
211+ ```
212+
213+ Call ` stream(...) ` again with the same ` stream_key ` whenever the state changes.
214+
215+ #### End a stream
216+
217+ Use this when the tracked process is finished and you no longer want the Live
218+ Activity on devices. ` content_state ` is optional here; include it if you want
219+ to end the stream with a final state.
220+
221+ ``` python
222+ activitysmith.live_activities.end_stream(
223+ " prod-web-1" ,
224+ {
225+ " content_state" : {
226+ " title" : " Server Health" ,
227+ " subtitle" : " prod-web-1" ,
228+ " type" : " metrics" ,
229+ " metrics" : [
230+ {" label" : " CPU" , " value" : 7 , " unit" : " %" },
231+ {" label" : " MEM" , " value" : 38 , " unit" : " %" },
232+ ],
233+ },
234+ },
235+ )
236+ ```
237+
238+ If you later send another ` stream(...) ` request with the same ` stream_key ` ,
239+ ActivitySmith starts a new Live Activity for that stream again.
240+
241+ Stream responses include an ` operation ` field:
122242
123- - ` segmented_progress ` : best for jobs tracked in steps
124- - ` progress ` : best for jobs tracked as a percentage or numeric range
243+ - ` started ` : ActivitySmith started a new Live Activity for this ` stream_key `
244+ - ` updated ` : ActivitySmith updated the current Live Activity
245+ - ` rotated ` : ActivitySmith ended the previous Live Activity and started a new one
246+ - ` noop ` : the incoming state matched the current state, so no update was sent
247+ - ` paused ` : the stream is paused, so no Live Activity was started or updated
248+ - ` ended ` : returned by ` end_stream(...) ` after the stream is ended
125249
126- ### Shared flow
250+ ### Advanced: Manual lifecycle control
251+
252+ Use these methods when you want to manage the Live Activity lifecycle yourself.
253+
254+ #### Shared flow
127255
1282561 . Call ` activitysmith.live_activities.start(...) ` .
1292572 . Save the returned ` activity_id ` .
1302583 . Call ` activitysmith.live_activities.update(...) ` as progress changes.
1312594 . Call ` activitysmith.live_activities.end(...) ` when the work is finished.
132260
261+ ### Metrics Type
262+
263+ Use ` metrics ` when you want to keep a small set of live stats visible, such as
264+ server health, queue pressure, or database load.
265+
266+ #### Start
267+
268+ <p align =" center " >
269+ <img src =" https://cdn.activitysmith.com/features/metrics-live-activity-start.png " alt =" Metrics start example " width =" 680 " />
270+ </p >
271+
272+ ``` python
273+ start = activitysmith.live_activities.start(
274+ {
275+ " content_state" : {
276+ " title" : " Server Health" ,
277+ " subtitle" : " prod-web-1" ,
278+ " type" : " metrics" ,
279+ " metrics" : [
280+ {" label" : " CPU" , " value" : 9 , " unit" : " %" },
281+ {" label" : " MEM" , " value" : 45 , " unit" : " %" },
282+ ],
283+ },
284+ }
285+ )
286+
287+ activity_id = start.activity_id
288+ ```
289+
290+ #### Update
291+
292+ <p align =" center " >
293+ <img src =" https://cdn.activitysmith.com/features/metrics-live-activity-update.png " alt =" Metrics update example " width =" 680 " />
294+ </p >
295+
296+ ``` python
297+ activitysmith.live_activities.update(
298+ {
299+ " activity_id" : activity_id,
300+ " content_state" : {
301+ " title" : " Server Health" ,
302+ " subtitle" : " prod-web-1" ,
303+ " type" : " metrics" ,
304+ " metrics" : [
305+ {" label" : " CPU" , " value" : 76 , " unit" : " %" },
306+ {" label" : " MEM" , " value" : 52 , " unit" : " %" },
307+ ],
308+ },
309+ }
310+ )
311+ ```
312+
313+ #### End
314+
315+ <p align =" center " >
316+ <img src =" https://cdn.activitysmith.com/features/metrics-live-activity-end.png " alt =" Metrics end example " width =" 680 " />
317+ </p >
318+
319+ ``` python
320+ activitysmith.live_activities.end(
321+ {
322+ " activity_id" : activity_id,
323+ " content_state" : {
324+ " title" : " Server Health" ,
325+ " subtitle" : " prod-web-1" ,
326+ " type" : " metrics" ,
327+ " metrics" : [
328+ {" label" : " CPU" , " value" : 7 , " unit" : " %" },
329+ {" label" : " MEM" , " value" : 38 , " unit" : " %" },
330+ ],
331+ " auto_dismiss_minutes" : 2 ,
332+ },
333+ }
334+ )
335+ ```
336+
133337### Segmented Progress Type
134338
135339Use ` segmented_progress ` when progress is easier to follow as steps instead of a
136340raw percentage. It fits jobs like backups, deployments, ETL pipelines, and
137- checklists where "step 2 of 3" is more useful than "67%".
138- ` number_of_steps ` is dynamic, so you can increase or decrease it later if the
139- workflow changes.
341+ checklists where "step 2 of 3" is more useful than "67%". ` number_of_steps ` is
342+ dynamic, so you can increase or decrease it later if the workflow changes.
140343
141344#### Start
142345
@@ -155,7 +358,6 @@ start = activitysmith.live_activities.start(
155358 " type" : " segmented_progress" ,
156359 " color" : " yellow" ,
157360 },
158- " channels" : [" devs" , " ops" ], # Optional
159361 }
160362)
161363
@@ -175,9 +377,9 @@ activitysmith.live_activities.update(
175377 " content_state" : {
176378 " title" : " Nightly database backup" ,
177379 " subtitle" : " upload archive" ,
178- " number_of_steps" : 4 ,
380+ " number_of_steps" : 3 ,
179381 " current_step" : 2 ,
180- }
382+ },
181383 }
182384)
183385```
@@ -195,10 +397,10 @@ activitysmith.live_activities.end(
195397 " content_state" : {
196398 " title" : " Nightly database backup" ,
197399 " subtitle" : " verify restore" ,
198- " number_of_steps" : 4 ,
199- " current_step" : 4 ,
400+ " number_of_steps" : 3 ,
401+ " current_step" : 3 ,
200402 " auto_dismiss_minutes" : 2 ,
201- }
403+ },
202404 }
203405)
204406```
@@ -223,7 +425,6 @@ start = activitysmith.live_activities.start(
223425 " subtitle" : " Added 30 mi range" ,
224426 " type" : " progress" ,
225427 " percentage" : 15 ,
226- " color" : " lime" ,
227428 }
228429 }
229430)
@@ -272,10 +473,10 @@ activitysmith.live_activities.end(
272473
273474### Live Activity Action
274475
275- 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.
476+ Just like Actionable Push Notifications, Live Activities can have a button that opens a URL in a browser or triggers a webhook. Webhooks are executed by the ActivitySmith backend.
276477
277478<p align =" center " >
278- <img src =" https://cdn.activitysmith.com/features/live-activity-with- action.png?v=20260319-1 " alt =" Live Activity with action " width =" 680 " />
479+ <img src =" https://cdn.activitysmith.com/features/metrics- live-activity-action.png " alt =" Metrics Live Activity with action" width =" 680 " />
279480</p >
280481
281482#### Open URL action
@@ -284,16 +485,18 @@ Just like Actionable Push Notifications, Live Activities can have a button that
284485start = activitysmith.live_activities.start(
285486 {
286487 " content_state" : {
287- " title" : " Deploying payments-api" ,
288- " subtitle" : " Running database migrations" ,
289- " number_of_steps" : 5 ,
290- " current_step" : 3 ,
291- " type" : " segmented_progress" ,
488+ " title" : " Server Health" ,
489+ " subtitle" : " prod-web-1" ,
490+ " type" : " metrics" ,
491+ " metrics" : [
492+ {" label" : " CPU" , " value" : 76 , " unit" : " %" },
493+ {" label" : " MEM" , " value" : 52 , " unit" : " %" },
494+ ],
292495 },
293496 " action" : {
294- " title" : " Open Workflow " ,
497+ " title" : " Open Dashboard " ,
295498 " type" : " open_url" ,
296- " url" : " https://github. com/acme/payments-api/actions/runs/1234567890 " ,
499+ " url" : " https://ops.example. com/servers/prod-web-1 " ,
297500 },
298501 }
299502)
@@ -308,18 +511,21 @@ activitysmith.live_activities.update(
308511 {
309512 " activity_id" : activity_id,
310513 " content_state" : {
311- " title" : " Reindexing product search" ,
312- " subtitle" : " Shard 7 of 12" ,
313- " number_of_steps" : 12 ,
314- " current_step" : 7 ,
514+ " title" : " Server Health" ,
515+ " subtitle" : " prod-web-1" ,
516+ " type" : " metrics" ,
517+ " metrics" : [
518+ {" label" : " CPU" , " value" : 91 , " unit" : " %" },
519+ {" label" : " MEM" , " value" : 57 , " unit" : " %" },
520+ ],
315521 },
316522 " action" : {
317- " title" : " Pause Reindex " ,
523+ " title" : " Restart Service " ,
318524 " type" : " webhook" ,
319- " url" : " https://ops.example.com/hooks/search/reindex/pause " ,
525+ " url" : " https://ops.example.com/hooks/servers/prod-web-1/restart " ,
320526 " method" : " POST" ,
321527 " body" : {
322- " job_id " : " reindex-2026-03-19 " ,
528+ " server_id " : " prod-web-1 " ,
323529 " requested_by" : " activitysmith-python" ,
324530 },
325531 },
0 commit comments