Skip to main content

Installation

Install the ActivitySmith Go SDK with go get:
Go
go get github.com/ActivitySmithHQ/activitysmith-go

Usage

  1. Create an API key
  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
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. Push notification example for a new subscription event
Go
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

Rich push notification with image
Go
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. Rich push notification with audio 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

Actionable push notification with redirection and actions 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
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

Metrics Live Activity screenshot

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

Metrics stream example

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

Segmented progress stream example

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

Progress stream example

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.
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

Metrics start example

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

Metrics update example

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

Metrics end example

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

Segmented progress start example

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

Segmented progress update example

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

Segmented progress end example

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

Progress start example

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

Progress update example

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

Progress end example

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. Live Activity with action

Open URL action

Go
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
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
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

Source Code

View the Go SDK source on GitHub