AlarmManager PRO

AlarmManager (iOS 26+) is a global API provided by Scripting for creating and managing AlarmKit-based alarms, timers, and countdown reminders. It does not need to be imported and can be used directly in scripts.

This API is suitable for scenarios such as:

  • Creating one-time alarms, such as “remind me tonight at 10:30 PM”
  • Creating daily or weekly recurring alarms
  • Creating Pomodoro timers, workout timers, and countdown-based reminders
  • Customizing alert titles, buttons, colors, icons, and metadata
  • Using AppIntent to handle alert button actions
  • Listening for alarm state changes and updating UI accordingly

Availability

Before using AlarmManager, you should first check whether the current environment supports AlarmKit:

if (!AlarmManager.isAvailable) {
  console.log("AlarmKit is not available on this device")
}

If the current device or system version does not support it, related methods may not work.


Core Concepts

AlarmManager is built around these main types:

  • Alarm: an existing alarm instance
  • Schedule: the schedule rule that determines when an alarm fires
  • Countdown: countdown-related parameters
  • Button: button styling for the alert UI
  • Sound: alert sound configuration
  • AlertPresentation: content shown when the alarm is alerting
  • CountdownPresentation: content shown while countdown is running
  • PausedPresentation: content shown while countdown is paused
  • Attributes: the overall presentation configuration
  • Configuration: the final configuration object used to create alarms, timers, or countdown reminders

Alarm ID Rules

The id passed to an alarm must be generated with:

UUID.string()

Do not manually build fixed IDs, and do not reuse old IDs. Every new alarm, timer, or countdown reminder should use a newly generated UUID string.

Correct example:

const id = UUID.string()

Not recommended:

const id = "morning-alarm"
const id = `timer-${Date.now()}`

All later operations such as cancel, stop, pause, resume, and startCountdown must use the exact same ID that was used when the alarm was created.


AlarmState

type AlarmState = "scheduled" | "countdown" | "paused" | "alerting"

Represents the current state of an alarm.

"scheduled"

The alarm has been scheduled and is waiting to fire.

Common cases:

  • Fixed-time alarms
  • Weekly recurring alarms
  • Countdown-based alarms that have not started yet

"countdown"

A countdown is currently running.

Common cases:

  • An active timer
  • A started countdown reminder

"paused"

The countdown is currently paused.

"alerting"

The alarm has fired and is currently presenting its alert.


SecondaryButtonBehavior

type SecondaryButtonBehavior = "countdown" | "custom"

Controls the behavior of the secondary button shown in the alert UI.

"countdown"

The secondary button starts a countdown flow.

This is typically used for “remind me later” or “remind me again in a few minutes” behavior.

"custom"

The secondary button is treated as a custom action button, and its actual behavior is defined by secondaryIntent.


AlarmAppIntent

type AlarmAppIntent = AppIntent<any, AppIntentProtocol.LiveActivityIntent>

Represents an AppIntent that can be attached to alarm buttons.

It is mainly used for:

  • Running additional logic when the stop button is tapped
  • Running custom logic when the secondary button is tapped
  • Integrating alarm interactions with Live Activity, Widget, or ControlWidget logic

It can be assigned through:

  • Configuration.alarm(...).stopIntent
  • Configuration.alarm(...).secondaryIntent
  • Configuration.timer(...).stopIntent
  • Configuration.timer(...).secondaryIntent
  • Configuration.countdown(...).stopIntent
  • Configuration.countdown(...).secondaryIntent

Note that the required protocol type here is:

AppIntentProtocol.LiveActivityIntent

That means any AppIntent used with AlarmManager should be registered with the LiveActivityIntent protocol.

A full example is provided later in this document.


AlarmUpdateListener

type AlarmUpdateListener = (alarms: Alarm[]) => void

A callback used to listen for alarm list updates.

When alarms are created, cancelled, paused, resumed, started, or otherwise updated, the listener receives the latest Alarm[].


Alarm

class Alarm {
  readonly id: string
  readonly state: AlarmState
  readonly schedule?: Schedule | null
  readonly countdownDuration?: Countdown | null
}

Represents an existing alarm object.

id

readonly id: string

The unique identifier of the alarm.

It must be generated with UUID.string() when the alarm is created. All later operations such as cancel, stop, pause, and resume depend on this value.

state

readonly state: AlarmState

The current alarm state.

schedule

readonly schedule?: Schedule | null

The scheduling rule for this alarm.

Pure timers may not have a schedule.

countdownDuration

readonly countdownDuration?: Countdown | null

The countdown-related configuration for this alarm.


Schedule

class Schedule {
  readonly type: "fixed" | "relative"
  readonly date?: Date | null
  readonly hour?: number | null
  readonly minute?: number | null
  readonly weekdays?: number[] | null

  static fixed(date: Date): Schedule
  static relative(hour: number, minute: number): Schedule
  static weekly(hour: number, minute: number, weekdays: number[]): Schedule
}

Describes when an alarm should fire.

type

readonly type: "fixed" | "relative"

The schedule type.

  • "fixed": a fixed absolute time
  • "relative": a schedule based on hour and minute

date

readonly date?: Date | null

The exact trigger date for a fixed-time schedule.

hour

readonly hour?: number | null

The hour component of the schedule.

minute

readonly minute?: number | null

The minute component of the schedule.

weekdays

readonly weekdays?: number[] | null

The weekday array for weekly recurring schedules.

The exact weekday number mapping depends on your underlying implementation. Typically this uses values from 1 to 7.

Schedule.fixed(date)

static fixed(date: Date): Schedule

Creates a fixed-time schedule.

const schedule = AlarmManager.Schedule.fixed(
  new Date("2026-03-20T22:30:00")
)

Schedule.relative(hour, minute)

static relative(hour: number, minute: number): Schedule

Creates a schedule based on hour and minute.

This is typically suitable for “every day at a given time” scenarios.

const schedule = AlarmManager.Schedule.relative(7, 30)

Schedule.weekly(hour, minute, weekdays)

static weekly(hour: number, minute: number, weekdays: number[]): Schedule

Creates a weekly recurring schedule.

const schedule = AlarmManager.Schedule.weekly(8, 0, [2, 3, 4, 5, 6])

Countdown

class Countdown {
  readonly preAlert?: number | null
  readonly postAlert?: number | null

  static create(options?: {
    preAlert?: DurationInSeconds | null
    postAlert?: DurationInSeconds | null
  }): Countdown
}

Defines countdown-related parameters.

preAlert

readonly preAlert?: number | null

The countdown duration before the alert, in seconds.

postAlert

readonly postAlert?: number | null

The additional duration after the alert, in seconds.

The exact behavior depends on your underlying implementation.

Countdown.create(options?)

static create(options?: {
  preAlert?: DurationInSeconds | null
  postAlert?: DurationInSeconds | null
}): Countdown

Creates a countdown configuration object.

const countdown = AlarmManager.Countdown.create({
  preAlert: 10 * 60,
  postAlert: 5 * 60
})

Button

class Button {
  static create(options: {
    title?: string
    textColor?: Color
    systemImageName?: string
  }): Button
}

Defines the appearance of a button shown in the alert UI.

Parameters

title

The button title.

textColor

The button text color.

In this documentation, Color should be provided as a hex string, for example:

"#ffffff"
"#ff9500"
"#34c759"

systemImageName

The SF Symbols name to use for the button icon.

Button.create(options)

static create(options: {
  title?: string
  textColor?: Color
  systemImageName?: string
}): Button
const stopButton = AlarmManager.Button.create({
  title: "Stop",
  textColor: "#ffffff",
  systemImageName: "stop.fill"
})

const snoozeButton = AlarmManager.Button.create({
  title: "Later",
  textColor: "#ffffff",
  systemImageName: "timer"
})

Sound

class Sound {
  static default(): Sound
  static named(name: string): Sound
}

Defines the alert sound.

Sound.default()

static default(): Sound

Uses the system default sound.

const sound = AlarmManager.Sound.default()

Sound.named(name)

static named(name: string): Sound

Uses a sound with the specified name.

const sound = AlarmManager.Sound.named("bell")

Available names depend on your underlying implementation and system support.


AlertPresentation

class AlertPresentation {
  static create(options: {
    title: string
    stopButton?: Button | null
    secondaryButton?: Button | null
    secondaryBehavior?: SecondaryButtonBehavior | null
  }): AlertPresentation
}

Defines the UI shown when the alarm is alerting.

Parameters

title

The alert title. This is required.

stopButton

Deprecated. Starting with iOS 26.1 the alert's stop action is rendered by the system as a slider, not as a custom button. stopButton is kept only for backward compatibility and is silently ignored on iOS 26.1+. You can omit it.

The stop button appearance (iOS 26.0 fallback only).

secondaryButton

The secondary button appearance.

secondaryBehavior

The behavior of the secondary button. Possible values:

  • "countdown"
  • "custom"

AlertPresentation.create(options)

static create(options: {
  title: string
  stopButton?: Button | null
  secondaryButton?: Button | null
  secondaryBehavior?: SecondaryButtonBehavior | null
}): AlertPresentation
const alert = AlarmManager.AlertPresentation.create({
  title: "Wake up",
  stopButton: AlarmManager.Button.create({
    title: "Dismiss",
    textColor: "#ffffff",
    systemImageName: "xmark"
  }),
  secondaryButton: AlarmManager.Button.create({
    title: "Remind me later",
    textColor: "#ffffff",
    systemImageName: "timer"
  }),
  secondaryBehavior: "countdown"
})

CountdownPresentation

class CountdownPresentation {
  static create(title?: string | null, pauseButton?: Button | null): CountdownPresentation
}

Defines the UI shown while a countdown is running.

CountdownPresentation.create(title?, pauseButton?)

static create(title?: string | null, pauseButton?: Button | null): CountdownPresentation
const countdownPresentation = AlarmManager.CountdownPresentation.create(
  "Focus session in progress",
  AlarmManager.Button.create({
    title: "Pause",
    textColor: "#ffffff",
    systemImageName: "pause.fill"
  })
)

PausedPresentation

class PausedPresentation {
  static create(title?: string | null, resumeButton?: Button | null): PausedPresentation | null
}

Defines the UI shown while a countdown is paused.

PausedPresentation.create(title?, resumeButton?)

static create(title?: string | null, resumeButton?: Button | null): PausedPresentation | null
const pausedPresentation = AlarmManager.PausedPresentation.create(
  "Paused",
  AlarmManager.Button.create({
    title: "Resume",
    textColor: "#ffffff",
    systemImageName: "play.fill"
  })
)

Attributes

class Attributes {
  static create(options: {
    alert: AlertPresentation
    countdown?: CountdownPresentation | null
    paused?: PausedPresentation | null
    tintColor?: Color
    metadata?: Record<string, string>
  }): Attributes | null
}

Combines the full presentation configuration for an alarm.

Parameters

alert

The alert presentation shown when the alarm fires. This is required.

countdown

The presentation shown while countdown is running.

paused

The presentation shown while countdown is paused.

tintColor

The main tint color. Hex strings are recommended, such as:

"#ffffff"
"#007aff"
"#ff9500"

metadata

Additional metadata for your own business logic.

Attributes.create(options)

static create(options: {
  alert: AlertPresentation
  countdown?: CountdownPresentation | null
  paused?: PausedPresentation | null
  tintColor?: Color
  metadata?: Record<string, string>
}): Attributes | null
const attributes = AlarmManager.Attributes.create({
  alert: AlarmManager.AlertPresentation.create({
    title: "Pomodoro finished",
    stopButton: AlarmManager.Button.create({
      title: "Done",
      textColor: "#ffffff",
      systemImageName: "checkmark"
    })
  }),
  countdown: AlarmManager.CountdownPresentation.create(
    "Focusing",
    AlarmManager.Button.create({
      title: "Pause",
      textColor: "#ffffff",
      systemImageName: "pause.fill"
    })
  ),
  paused: AlarmManager.PausedPresentation.create(
    "Paused",
    AlarmManager.Button.create({
      title: "Resume",
      textColor: "#ffffff",
      systemImageName: "play.fill"
    })
  ),
  tintColor: "#ff9500",
  metadata: {
    type: "pomodoro",
    source: "study-script"
  }
})

Configuration

class Configuration {
  static alarm(options: {
    schedule?: Schedule | null
    attributes: Attributes
    sound?: Sound | null
    stopIntent?: AlarmAppIntent | null
    secondaryIntent?: AlarmAppIntent | null
  }): Configuration | null

  static timer(options: {
    duration: DurationInSeconds
    attributes: Attributes
    sound?: Sound | null
    stopIntent?: AlarmAppIntent | null
    secondaryIntent?: AlarmAppIntent | null
  }): Configuration | null

  static countdown(options: {
    countdown?: Countdown | null
    schedule?: Schedule | null
    attributes: Attributes
    sound?: Sound | null
    stopIntent?: AlarmAppIntent | null
    secondaryIntent?: AlarmAppIntent | null
  }): Configuration | null
}

Creates the final configuration object passed to AlarmManager.schedule().


Configuration.alarm

static alarm(options: {
  schedule?: Schedule | null
  attributes: Attributes
  sound?: Sound | null
  stopIntent?: AlarmAppIntent | null
  secondaryIntent?: AlarmAppIntent | null
}): Configuration | null

Creates a regular alarm configuration.

Suitable for:

  • One-time alarms
  • Daily alarms
  • Weekly recurring alarms

Parameters

schedule

The alarm schedule.

attributes

The presentation attributes. Required.

sound

The alert sound.

stopIntent

An AppIntent bound to the stop button.

secondaryIntent

An AppIntent bound to the secondary button.

Example

const schedule = AlarmManager.Schedule.fixed(
  new Date("2026-03-20T07:30:00")
)

const attributes = AlarmManager.Attributes.create({
  alert: AlarmManager.AlertPresentation.create({
    title: "Good morning, time to wake up",
    stopButton: AlarmManager.Button.create({
      title: "Dismiss",
      textColor: "#ffffff",
      systemImageName: "xmark"
    }),
    secondaryButton: AlarmManager.Button.create({
      title: "Later",
      textColor: "#ffffff",
      systemImageName: "timer"
    }),
    secondaryBehavior: "countdown"
  }),
  tintColor: "#34c759",
  metadata: {
    category: "morning"
  }
})

const configuration = AlarmManager.Configuration.alarm({
  schedule,
  attributes,
  sound: AlarmManager.Sound.default()
})

Configuration.timer

static timer(options: {
  duration: DurationInSeconds
  attributes: Attributes
  sound?: Sound | null
  stopIntent?: AlarmAppIntent | null
  secondaryIntent?: AlarmAppIntent | null
}): Configuration | null

Creates a timer configuration.

Suitable for:

  • Pomodoro timers
  • Workout timers
  • Cooking reminders
  • Short focus sessions

Parameters

duration

The total duration of the timer, in seconds.

attributes

The presentation attributes. Required.

sound

The alert sound.

stopIntent

An AppIntent bound to the stop action.

secondaryIntent

An AppIntent bound to the secondary action.

Example

const attributes = AlarmManager.Attributes.create({
  alert: AlarmManager.AlertPresentation.create({
    title: "25-minute focus session finished",
    stopButton: AlarmManager.Button.create({
      title: "Done",
      textColor: "#ffffff",
      systemImageName: "checkmark"
    })
  }),
  countdown: AlarmManager.CountdownPresentation.create(
    "Focusing",
    AlarmManager.Button.create({
      title: "Pause",
      textColor: "#ffffff",
      systemImageName: "pause.fill"
    })
  ),
  paused: AlarmManager.PausedPresentation.create(
    "Paused",
    AlarmManager.Button.create({
      title: "Resume",
      textColor: "#ffffff",
      systemImageName: "play.fill"
    })
  ),
  tintColor: "#ff9500",
  metadata: {
    mode: "focus"
  }
})

const configuration = AlarmManager.Configuration.timer({
  duration: 25 * 60,
  attributes,
  sound: AlarmManager.Sound.default()
})

Configuration.countdown

static countdown(options: {
  countdown?: Countdown | null
  schedule?: Schedule | null
  attributes: Attributes
  sound?: Sound | null
  stopIntent?: AlarmAppIntent | null
  secondaryIntent?: AlarmAppIntent | null
}): Configuration | null

Creates a countdown reminder configuration.

Suitable for:

  • Triggering a countdown after a scheduled time
  • “Remind me later” flows
  • Custom pre-alert and post-alert countdown behavior

Parameters

countdown

The countdown configuration.

schedule

The schedule rule.

attributes

The presentation attributes. Required.

sound

The alert sound.

stopIntent

An AppIntent bound to the stop action.

secondaryIntent

An AppIntent bound to the secondary action.

Example

const countdown = AlarmManager.Countdown.create({
  preAlert: 15 * 60,
  postAlert: 5 * 60
})

const schedule = AlarmManager.Schedule.relative(21, 0)

const attributes = AlarmManager.Attributes.create({
  alert: AlarmManager.AlertPresentation.create({
    title: "Get ready to rest",
    stopButton: AlarmManager.Button.create({
      title: "OK",
      textColor: "#ffffff",
      systemImageName: "bed.double.fill"
    }),
    secondaryButton: AlarmManager.Button.create({
      title: "Remind me in 15 minutes",
      textColor: "#ffffff",
      systemImageName: "timer"
    }),
    secondaryBehavior: "countdown"
  }),
  countdown: AlarmManager.CountdownPresentation.create(
    "Rest countdown in progress",
    AlarmManager.Button.create({
      title: "Pause",
      textColor: "#ffffff",
      systemImageName: "pause.fill"
    })
  ),
  paused: AlarmManager.PausedPresentation.create(
    "Paused",
    AlarmManager.Button.create({
      title: "Resume",
      textColor: "#ffffff",
      systemImageName: "play.fill"
    })
  ),
  tintColor: "#af52de",
  metadata: {
    scene: "night"
  }
})

const configuration = AlarmManager.Configuration.countdown({
  countdown,
  schedule,
  attributes,
  sound: AlarmManager.Sound.default()
})

isAvailable

const isAvailable: boolean

Indicates whether AlarmKit is available in the current environment.

if (!AlarmManager.isAvailable) {
  throw new Error("AlarmManager is not available")
}

alarms

function alarms(): Promise<Alarm[]>

Returns all current alarms.

Return Value

Returns Promise<Alarm[]>.

Example

const list = await AlarmManager.alarms()

for (const alarm of list) {
  console.log(alarm.id, alarm.state)
}

schedule

function schedule(id: string, configuration: Configuration): Promise<Alarm>

Creates and schedules an alarm using the provided configuration.

Parameters

id

The unique alarm ID.

It must be generated with UUID.string().

configuration

A configuration created by AlarmManager.Configuration.alarm(), timer(), or countdown().

Return Value

Returns the created Alarm.

Example

const configuration = AlarmManager.Configuration.timer({
  duration: 10 * 60,
  attributes: AlarmManager.Attributes.create({
    alert: AlarmManager.AlertPresentation.create({
      title: "10 minutes are up",
      stopButton: AlarmManager.Button.create({
        title: "Stop",
        textColor: "#ffffff",
        systemImageName: "stop.fill"
      })
    }),
    countdown: AlarmManager.CountdownPresentation.create(
      "Counting down",
      AlarmManager.Button.create({
        title: "Pause",
        textColor: "#ffffff",
        systemImageName: "pause.fill"
      })
    ),
    paused: AlarmManager.PausedPresentation.create(
      "Paused",
      AlarmManager.Button.create({
        title: "Resume",
        textColor: "#ffffff",
        systemImageName: "play.fill"
      })
    ),
    tintColor: "#007aff"
  }),
  sound: AlarmManager.Sound.default()
})

const id = UUID.string()
const alarm = await AlarmManager.schedule(id, configuration)

console.log(alarm.id, alarm.state)

cancel

function cancel(id: string): Promise<boolean>

Cancels the specified alarm.

Parameters

id

The alarm ID to cancel.

Return Value

Returns whether the operation succeeded.

Example

const success = await AlarmManager.cancel(alarmId)
console.log("Cancel result:", success)

stop

function stop(id: string): Promise<boolean>

Stops the specified alarm or alert.

This is typically used when an alarm is already alerting, or when the current flow should be terminated.

Example

const success = await AlarmManager.stop(alarmId)
console.log("Stop result:", success)

pause

function pause(id: string): Promise<boolean>

Pauses the specified countdown or timer.

Example

const success = await AlarmManager.pause(alarmId)
console.log("Pause result:", success)

resume

function resume(id: string): Promise<boolean>

Resumes a paused countdown or timer.

Example

const success = await AlarmManager.resume(alarmId)
console.log("Resume result:", success)

startCountdown

function startCountdown(id: string): Promise<boolean>

Starts the countdown flow for the specified alarm.

This is usually used with alarms created using Configuration.countdown() or flows that include “remind me later” behavior.

Example

const success = await AlarmManager.startCountdown(alarmId)
console.log("Start countdown result:", success)

addAlarmUpdateListener

function addAlarmUpdateListener(listener: AlarmUpdateListener): void

Adds an alarm update listener.

When the alarm list changes, the callback receives the latest Alarm[].

Example

const listener = (alarms: AlarmManager.Alarm[]) => {
  console.log("Alarm count:", alarms.length)
  for (const alarm of alarms) {
    console.log(alarm.id, alarm.state)
  }
}

AlarmManager.addAlarmUpdateListener(listener)

removeAlarmUpdateListener

function removeAlarmUpdateListener(listener?: AlarmUpdateListener): void

Removes a previously added listener, or removes all listeners if no argument is provided.

Parameters

listener

The listener function to remove.

If this argument is omitted, the exact behavior depends on your underlying implementation.

Example

const listener = (alarms: AlarmManager.Alarm[]) => {
  console.log("Updated:", alarms.length)
}

AlarmManager.addAlarmUpdateListener(listener)

AlarmManager.removeAlarmUpdateListener(listener)

Using AppIntent with AlarmManager

AlarmManager supports AppIntent through stopIntent and secondaryIntent.

This allows alert buttons to do more than just stop an alert or start a countdown. They can also run custom script logic, for example:

  • Marking a task as completed when the user taps Stop
  • Updating local state when an alert is dismissed
  • Refreshing Widgets or Live Activities after “Later” is tapped
  • Triggering additional business logic through global APIs

Where AppIntent Must Be Defined

All AppIntent definitions must be placed in:

app_intents.tsx

Do not define them in widget.tsx, live_activity.tsx, control_widget.tsx, or regular script files.

When an AppIntent runs:

Script.env === "app_intents"

That means the perform function always executes in the app_intents environment.


AppIntent Types

AppIntent

Represents a concrete app intent instance.

FieldTypeDescription
scriptstringThe script path, generated internally
namestringThe unique intent name
protocolAppIntentProtocolThe protocol type used by the intent
paramsTThe parameters passed when the intent is triggered

AppIntentFactory

type AppIntentFactory<T> = (params: T) => AppIntent<T>

A factory function that creates an AppIntent instance from parameters.

AppIntentPerform

type AppIntentPerform<T> = (params: T) => Promise<void>

The async function that runs when the intent is triggered.

AppIntentProtocol

Enum MemberDescription
AppIntentA regular app intent
AudioPlaybackIntentAn intent for controlling audio playback
AudioRecordingIntentAn intent for controlling audio recording
LiveActivityIntentAn intent for starting, pausing, or updating Live Activities

For AlarmManager, you should use:

AppIntentProtocol.LiveActivityIntent

AppIntentManager.register

static register<T = undefined>(options: {
  name: string
  protocol: AppIntentProtocol
  perform: AppIntentPerform<T>
}): AppIntentFactory<T>

Registers a new AppIntent.

Parameters

name

The unique intent name.

protocol

The protocol type for the intent.

perform

The async function that runs when the intent is triggered.

Return Value

Returns a factory function that creates the corresponding AppIntent instance.


AppIntent Example for AlarmManager

The example below shows how to define two intents in app_intents.tsx for use with AlarmManager.

/// app_intents.tsx

export const StopFocusAlarmIntent = AppIntentManager.register({
  name: "StopFocusAlarmIntent",
  protocol: AppIntentProtocol.LiveActivityIntent,
  perform: async ({ alarmId, taskId }: { alarmId: string; taskId: string }) => {
    console.log("Stopping alert:", alarmId, taskId)

    // Your custom logic goes here
    // For example, update task state, save logs, refresh UI, etc.
    await Storage.set(`task:${taskId}:finished`, true)

    Widget.reloadAll()
  }
})

export const SnoozeFocusAlarmIntent = AppIntentManager.register({
  name: "SnoozeFocusAlarmIntent",
  protocol: AppIntentProtocol.LiveActivityIntent,
  perform: async ({ alarmId }: { alarmId: string }) => {
    console.log("Snoozing alert:", alarmId)

    Widget.reloadAll()
  }
})

Binding AppIntent to AlarmManager

Once the intents are defined, they can be attached through stopIntent and secondaryIntent.

const alarmId = UUID.string()

const attributes = AlarmManager.Attributes.create({
  alert: AlarmManager.AlertPresentation.create({
    title: "Focus session finished",
    stopButton: AlarmManager.Button.create({
      title: "Done",
      textColor: "#ffffff",
      systemImageName: "checkmark"
    }),
    secondaryButton: AlarmManager.Button.create({
      title: "Snooze 5 min",
      textColor: "#ffffff",
      systemImageName: "timer"
    }),
    secondaryBehavior: "custom"
  }),
  countdown: AlarmManager.CountdownPresentation.create(
    "Counting down",
    AlarmManager.Button.create({
      title: "Pause",
      textColor: "#ffffff",
      systemImageName: "pause.fill"
    })
  ),
  paused: AlarmManager.PausedPresentation.create(
    "Paused",
    AlarmManager.Button.create({
      title: "Resume",
      textColor: "#ffffff",
      systemImageName: "play.fill"
    })
  ),
  tintColor: "#007aff",
  metadata: {
    type: "focus"
  }
})

const configuration = AlarmManager.Configuration.timer({
  duration: 25 * 60,
  attributes,
  sound: AlarmManager.Sound.default(),
  stopIntent: StopFocusAlarmIntent({
    alarmId,
    taskId: "task_001"
  }),
  secondaryIntent: SnoozeFocusAlarmIntent({
    alarmId
  })
})

await AlarmManager.schedule(alarmId, configuration)

In this example:

  • The stop button triggers StopFocusAlarmIntent
  • The secondary button triggers SnoozeFocusAlarmIntent
  • The secondary button uses "custom", so its behavior is fully defined by secondaryIntent

Using Countdown Behavior for the Secondary Button

If you want the secondary button to directly use AlarmKit’s countdown behavior, set:

secondaryBehavior: "countdown"

Example:

const alarmId = UUID.string()

const attributes = AlarmManager.Attributes.create({
  alert: AlarmManager.AlertPresentation.create({
    title: "Time to rest",
    stopButton: AlarmManager.Button.create({
      title: "Dismiss",
      textColor: "#ffffff",
      systemImageName: "xmark"
    }),
    secondaryButton: AlarmManager.Button.create({
      title: "Remind me in 10 minutes",
      textColor: "#ffffff",
      systemImageName: "timer"
    }),
    secondaryBehavior: "countdown"
  }),
  countdown: AlarmManager.CountdownPresentation.create(
    "Snooze countdown in progress",
    AlarmManager.Button.create({
      title: "Pause",
      textColor: "#ffffff",
      systemImageName: "pause.fill"
    })
  ),
  paused: AlarmManager.PausedPresentation.create(
    "Paused",
    AlarmManager.Button.create({
      title: "Resume",
      textColor: "#ffffff",
      systemImageName: "play.fill"
    })
  ),
  tintColor: "#ff3b30"
})

const configuration = AlarmManager.Configuration.countdown({
  countdown: AlarmManager.Countdown.create({
    preAlert: 10 * 60
  }),
  attributes,
  sound: AlarmManager.Sound.default()
})

await AlarmManager.schedule(alarmId, configuration)

This approach is better for standard snooze-style interactions.


Creating a One-Time Alarm

if (!AlarmManager.isAvailable) {
  throw new Error("AlarmKit is not available in the current environment")
}

const alarmId = UUID.string()

const schedule = AlarmManager.Schedule.fixed(
  new Date("2026-03-20T22:30:00")
)

const attributes = AlarmManager.Attributes.create({
  alert: AlarmManager.AlertPresentation.create({
    title: "Time to rest",
    stopButton: AlarmManager.Button.create({
      title: "Dismiss",
      textColor: "#ffffff",
      systemImageName: "xmark"
    }),
    secondaryButton: AlarmManager.Button.create({
      title: "Remind me in 10 minutes",
      textColor: "#ffffff",
      systemImageName: "timer"
    }),
    secondaryBehavior: "countdown"
  }),
  countdown: AlarmManager.CountdownPresentation.create(
    "Snooze countdown in progress",
    AlarmManager.Button.create({
      title: "Pause",
      textColor: "#ffffff",
      systemImageName: "pause.fill"
    })
  ),
  paused: AlarmManager.PausedPresentation.create(
    "Paused",
    AlarmManager.Button.create({
      title: "Resume",
      textColor: "#ffffff",
      systemImageName: "play.fill"
    })
  ),
  tintColor: "#ff3b30",
  metadata: {
    purpose: "sleep"
  }
})

const configuration = AlarmManager.Configuration.alarm({
  schedule,
  attributes,
  sound: AlarmManager.Sound.default()
})

const alarm = await AlarmManager.schedule(alarmId, configuration)
console.log("Created:", alarm.id, alarm.state)

Creating a Weekly Recurring Alarm

const alarmId = UUID.string()

const schedule = AlarmManager.Schedule.weekly(7, 30, [2, 3, 4, 5, 6])

const attributes = AlarmManager.Attributes.create({
  alert: AlarmManager.AlertPresentation.create({
    title: "Weekday wake-up reminder",
    stopButton: AlarmManager.Button.create({
      title: "Wake Up",
      textColor: "#ffffff",
      systemImageName: "sun.max.fill"
    })
  }),
  tintColor: "#34c759",
  metadata: {
    tag: "weekday-morning"
  }
})

const configuration = AlarmManager.Configuration.alarm({
  schedule,
  attributes,
  sound: AlarmManager.Sound.default()
})

await AlarmManager.schedule(alarmId, configuration)

Creating a Pomodoro Timer

const alarmId = UUID.string()

const attributes = AlarmManager.Attributes.create({
  alert: AlarmManager.AlertPresentation.create({
    title: "Your focus session has ended",
    stopButton: AlarmManager.Button.create({
      title: "Done",
      textColor: "#ffffff",
      systemImageName: "checkmark.circle.fill"
    })
  }),
  countdown: AlarmManager.CountdownPresentation.create(
    "Focusing",
    AlarmManager.Button.create({
      title: "Pause",
      textColor: "#ffffff",
      systemImageName: "pause.fill"
    })
  ),
  paused: AlarmManager.PausedPresentation.create(
    "Focus paused",
    AlarmManager.Button.create({
      title: "Resume",
      textColor: "#ffffff",
      systemImageName: "play.fill"
    })
  ),
  tintColor: "#ff9500",
  metadata: {
    type: "pomodoro",
    duration: "1500"
  }
})

const configuration = AlarmManager.Configuration.timer({
  duration: 25 * 60,
  attributes,
  sound: AlarmManager.Sound.default()
})

await AlarmManager.schedule(alarmId, configuration)

Creating a Manually Started Countdown Reminder

const alarmId = UUID.string()

const countdown = AlarmManager.Countdown.create({
  preAlert: 5 * 60,
  postAlert: 60
})

const attributes = AlarmManager.Attributes.create({
  alert: AlarmManager.AlertPresentation.create({
    title: "Snooze time is over",
    stopButton: AlarmManager.Button.create({
      title: "Got it",
      textColor: "#ffffff",
      systemImageName: "bell.slash.fill"
    })
  }),
  countdown: AlarmManager.CountdownPresentation.create(
    "Snooze in progress",
    AlarmManager.Button.create({
      title: "Pause",
      textColor: "#ffffff",
      systemImageName: "pause.fill"
    })
  ),
  paused: AlarmManager.PausedPresentation.create(
    "Countdown paused",
    AlarmManager.Button.create({
      title: "Resume",
      textColor: "#ffffff",
      systemImageName: "play.fill"
    })
  ),
  tintColor: "#af52de"
})

const configuration = AlarmManager.Configuration.countdown({
  countdown,
  attributes,
  sound: AlarmManager.Sound.default()
})

await AlarmManager.schedule(alarmId, configuration)
await AlarmManager.startCountdown(alarmId)

Querying and Controlling Alarms

const alarms = await AlarmManager.alarms()
console.log("Alarm count:", alarms.length)

const firstAlarm = alarms[0]
if (firstAlarm) {
  await AlarmManager.pause(firstAlarm.id)
  await AlarmManager.resume(firstAlarm.id)
  await AlarmManager.stop(firstAlarm.id)
}

Listening for Alarm State Changes

const listener = (alarms: AlarmManager.Alarm[]) => {
  const summary = alarms.map(item => ({
    id: item.id,
    state: item.state
  }))
  console.log("Latest alarm states:", JSON.stringify(summary, null, 2))
}

AlarmManager.addAlarmUpdateListener(listener)

// Remove when no longer needed
// AlarmManager.removeAlarmUpdateListener(listener)

Execution Environment

AlarmManager itself is a global API and can be used directly in regular scripts.

However, if you attach AppIntent objects to AlarmManager, those intent perform functions always run in:

Script.env === "app_intents"

That means:

  • Alarm creation logic such as AlarmManager.schedule() usually runs in a normal script environment
  • Logic for stopIntent and secondaryIntent runs in the perform functions defined in app_intents.tsx
  • Inside perform, you can safely do things like network requests, state updates, Widget refreshes, and Live Activity updates

Best Practices

Always use UUID.string() for alarmId

This is a strict requirement of AlarmManager.

const alarmId = UUID.string()

Keep the generated alarmId

Once an alarm is created, keep the alarmId if you need to pause, resume, stop, or cancel it later.

Store task IDs, source pages, scene names, or other app-specific values there.

metadata: {
  taskId: "task_123",
  scene: "study",
  source: "focus-page"
}

Keep all AppIntent definitions in app_intents.tsx

Do not scatter them across multiple files.

Use explicit parameter types for AppIntent

This improves type checking and editor completion.

perform: async ({ alarmId, taskId }: { alarmId: string; taskId: string }) => {
  // ...
}

Prefer countdown for standard snooze behavior

If you only need a normal “remind me again in a few minutes” flow, prefer:

secondaryBehavior: "countdown"

If you need completely custom logic, use:

secondaryBehavior: "custom"
secondaryIntent: ...

Notes

Attributes.create(...) may return null

const attributes = AlarmManager.Attributes.create(...)

Its return type is Attributes | null, so you should validate the result in stricter flows.

Configuration methods may also return null

const configuration = AlarmManager.Configuration.timer(...)

These methods also allow null, so validate before calling schedule().

pause, resume, and startCountdown are not meaningful for every alarm type

These methods are generally intended for timers or countdown-based flows. For fixed-time alarms, some operations may not make sense.


Full Example

The example below shows a complete flow that includes AppIntent.

/// app_intents.tsx

export const StopPomodoroIntent = AppIntentManager.register({
  name: "StopPomodoroIntent",
  protocol: AppIntentProtocol.LiveActivityIntent,
  perform: async ({ alarmId, sessionId }: { alarmId: string; sessionId: string }) => {
    console.log("Pomodoro finished:", alarmId, sessionId)
    await Storage.set(`pomodoro:${sessionId}:done`, true)
    Widget.reloadAll()
  }
})

export const ExtendPomodoroIntent = AppIntentManager.register({
  name: "ExtendPomodoroIntent",
  protocol: AppIntentProtocol.LiveActivityIntent,
  perform: async ({ alarmId, extraMinutes }: { alarmId: string; extraMinutes: number }) => {
    console.log("Extending Pomodoro:", alarmId, extraMinutes)
    Widget.reloadAll()
  }
})
/// regular script file

async function main() {
  if (!AlarmManager.isAvailable) {
    console.log("AlarmManager is not available")
    return
  }

  const listener = (alarms: AlarmManager.Alarm[]) => {
    console.log(
      "Alarm update:",
      alarms.map(item => `${item.id}:${item.state}`).join(", ")
    )
  }

  AlarmManager.addAlarmUpdateListener(listener)

  const alarmId = UUID.string()

  const attributes = AlarmManager.Attributes.create({
    alert: AlarmManager.AlertPresentation.create({
      title: "Focus session finished",
      stopButton: AlarmManager.Button.create({
        title: "Done",
        textColor: "#ffffff",
        systemImageName: "checkmark"
      }),
      secondaryButton: AlarmManager.Button.create({
        title: "Extend 5 min",
        textColor: "#ffffff",
        systemImageName: "timer"
      }),
      secondaryBehavior: "custom"
    }),
    countdown: AlarmManager.CountdownPresentation.create(
      "Focusing",
      AlarmManager.Button.create({
        title: "Pause",
        textColor: "#ffffff",
        systemImageName: "pause.fill"
      })
    ),
    paused: AlarmManager.PausedPresentation.create(
      "Paused",
      AlarmManager.Button.create({
        title: "Resume",
        textColor: "#ffffff",
        systemImageName: "play.fill"
      })
    ),
    tintColor: "#007aff",
    metadata: {
      feature: "focus-mode"
    }
  })

  if (!attributes) {
    throw new Error("Failed to create Attributes")
  }

  const configuration = AlarmManager.Configuration.timer({
    duration: 15 * 60,
    attributes,
    sound: AlarmManager.Sound.default(),
    stopIntent: StopPomodoroIntent({
      alarmId,
      sessionId: "session_001"
    }),
    secondaryIntent: ExtendPomodoroIntent({
      alarmId,
      extraMinutes: 5
    })
  })

  if (!configuration) {
    throw new Error("Failed to create Configuration")
  }

  const alarm = await AlarmManager.schedule(alarmId, configuration)
  console.log("Created alarm:", alarm.id, alarm.state)

  const current = await AlarmManager.alarms()
  console.log("Current alarms:", current.map(item => item.id))

  await AlarmManager.pause(alarmId)
  await AlarmManager.resume(alarmId)

  // Remove when finished
  // AlarmManager.removeAlarmUpdateListener(listener)
}

main().catch(error => {
  console.log(String(error))
})