RingSignals
RingSignals connects Ring doorbells and cameras to an agent. It receives Ring webhooks, delivers each event to an agent thread as a notification signal, and adds ring_snapshot and ring_clip tools so the agent can fetch media for an event. It also serves the routes Ring calls for account linking and webhooks.
For setup in the Ring Developer Console and on ring.com, see the Ring integration guide.
Usage exampleDirect link to Usage example
import { Agent } from '@mastra/core/agent'
import { Memory } from '@mastra/memory'
import { RingSignals } from '@mastra/ring-signals'
export const homeAgent = new Agent({
id: 'home-agent',
name: 'Home Agent',
instructions: 'Watch the front door and say briefly whether each event needs attention.',
model: 'openai/gpt-6.1-sol',
memory: new Memory(),
signals: [
new RingSignals({
events: {
device_offline: { priority: 'low', summary: 'Device went offline.', coalesce: true },
},
tools: {
clip: { visibleToModel: true },
},
}),
],
})
Linked accounts are saved in the channels storage domain. The Mastra instance needs a storage adapter.
Constructor parametersDirect link to Constructor parameters
All fields are optional. Credentials fall back to environment variables. If clientId, clientSecret, or hmacKey is missing, a warning is logged when the agent is registered, and account linking, webhooks, and media requests fail.
clientId?:
clientSecret?:
hmacKey?:
baseUrl?:
encryptionKey?:
mediaUrlTtlMs?:
routePrefix?:
userAgent?:
User-Agent header sent to the Ring API.hardwareId?:
Hardware_Id sent to the Ring API. By default, a UUID is generated when an account is linked and stored with that account's tokens.target?:
subscribeThread(). Pass a fixed target, or a function that receives the RingWebhookEvent and returns a target, or undefined to drop the event. The function can be async.events?:
false to disable it. See Events.priority:
summary:
coalesce?:
coalescedCount.attributes?:
null are dropped.instructions?:
false to leave it out.tools?:
false to remove it, or to an options object to configure it. See Tools.snapshot?:
ring_snapshot. Set to false to remove it.clip?:
ring_clip. Set to false to remove it.EventsDirect link to Events
Ring sends these event types. Events without an entry in DEFAULT_RING_EVENTS are disabled until you configure them in events.
| Event | Sent when | Default priority | Default summary |
|---|---|---|---|
button_press | The doorbell button is pressed | high | Doorbell was pressed. |
motion_detected | A device camera detects motion | medium | Motion was detected. |
device_added | A device becomes accessible to your app | Disabled | |
device_removed | A device is no longer accessible to your app | Disabled | |
device_online | A device comes online | Disabled | |
device_offline | A device goes offline | Disabled | |
app_integration_added | A Ring account links your app | Disabled | |
app_integration_removed | A Ring account unlinks your app | Disabled | |
subscription_activated | A Ring paid subscription or trial starts | Disabled | |
subscription_deactivated | A Ring paid subscription or trial ends | Disabled |
Device events have deviceId and timestamp attributes. Subscription events have no timestamp, and app integration and subscription events have no deviceId. Motion events also have motionType: human, vehicle, package_delivery, or other_motion when Ring Smart Alerts are on, and motion when they're off.
Some events also have side effects, whether or not they're enabled. app_integration_removed deletes the account's stored tokens, and subscription_deactivated logs a warning that media is no longer available.
RingWebhookEventDirect link to ringwebhookevent
The webhook payload Ring sends for each event. The target function and the attributes function in events receive it.
type RingWebhookEvent = {
meta: {
version: string
time: string
request_id: string
account_id?: string
}
data: {
id: string
type: RingEventType
attributes: {
source: string
source_id?: string
source_type?: string
timestamp?: number
sub_type?: RingMotionType | RingSubscriptionType
plan_id?: string | null
expires_at?: string
component_ids?: string[]
}
}
}
meta.account_id: The Ring account associated with the event.data.type: The event type, from the table above.data.attributes.source: The device ID for device events, the account ID forapp_integration_addedandapp_integration_removed, orsubscriptionsfor subscription events.data.attributes.source_id: The subscription ID, on subscription events.data.attributes.source_type: The kind of source:devicesfor device events,usersfor app integration events.data.attributes.timestamp: When the event happened, in epoch milliseconds. It's missing on subscription events.data.attributes.sub_type: What caused a motion event:human,vehicle,package_delivery,other_motion, ormotionwhen Smart Alerts are off.paidortrialon subscription events.data.attributes.plan_id: The subscription plan ID, on subscription events.nullfor trials.data.attributes.expires_at: When the billing cycle or trial ends, as an ISO 8601 date. Always present onsubscription_activated.data.attributes.component_ids: The cameras involved, on devices with more than one.
ToolsDirect link to Tools
ring_snapshotDirect link to ring_snapshot
Fetches a still image for an event. If Ring has no image right after the event, it uses the most recent image before it, up to 24 hours back.
Input: { deviceId: string, timestamp: number }, taken from the notification's attributes.
attachment?:
true attaches on every channel platform, false never attaches, and a list of platform names attaches only on those. Studio, web, and API threads never get attachments.maxAttachmentBytes?:
visibleToModel?:
ring_clipDirect link to ring_clip
Fetches a video clip that starts at the event. Ring needs time to process a recording. The tool retries until the clip is ready or processingTimeoutMs passes.
Input: { deviceId: string, timestamp: number }, taken from the notification's attributes.
attachment?:
maxAttachmentBytes?:
visibleToModel?:
audio?:
durationMs?:
processingTimeoutMs?:
Tool resultDirect link to Tool result
Both tools return the same shape:
mediaId:
url?:
baseUrl is set.attached:
RoutesDirect link to Routes
These routes are added to the Mastra server when the agent is registered. They don't use Mastra authentication. Webhooks are verified with Ring's signature, account links with Ring's link nonce, and media links with their signature and expiry time. The token route has no check of its own: it sends each code to Ring, and Ring rejects codes it didn't issue. Rate-limit <routePrefix>/token in your server's middleware.
Each instance needs its own routePrefix. When two instances use the same prefix, Mastra registers only the first instance's routes and logs a warning. The second instance gets no Ring events.
| Route | Purpose |
|---|---|
GET <routePrefix>/link | Account Link URL. Finishes linking and shows the result. |
GET <routePrefix>/home | App Homepage URL. Returns Ring Signals as plain text. |
POST <routePrefix>/token | Token Exchange URL. Exchanges an authorization code for tokens. |
POST <routePrefix>/webhook | Webhook URL. Receives Ring events. |
GET <routePrefix>/media/:kind/:deviceId/:timestamp | Serves signed snapshot and clip links. |
MethodsDirect link to Methods
subscribeThread(target, deviceId)Direct link to subscribethreadtarget-deviceid
Sends events from a Ring device to a specific thread. Devices without a subscription use the target option. Several threads can subscribe to the same device, and each one receives every event. Subscriptions are kept in memory and are lost when the server restarts.
ringSignals.subscribeThread({ resourceId: 'user-1', threadId: 'front-door' }, deviceId)
Returns: SignalSubscription
target:
threadId and resourceId.deviceId:
unsubscribeThread(target, deviceId)Direct link to unsubscribethreadtarget-deviceid
Stops sending events from a Ring device to a thread.
Returns: boolean, true if the thread was subscribed.
ExportsDirect link to Exports
@mastra/ring-signals also exports:
DEFAULT_RING_EVENTS: The defaulteventsconfiguration.DEFAULT_RING_INSTRUCTIONS: The default system instruction, for both tools.buildRingInstructions({ snapshot, clip }): Builds the system instruction for the enabled tools. Use it to extend the default text.RingApiClientandRingApiError: A client for the Ring Partner API and the error it throws.- Types:
RingSignalsOptions,RingEventType,RingEventNotification,RingMotionType,RingSubscriptionType,RingWebhookEvent,RingTargetResolver,RingToolsOptions,RingSnapshotToolOptions,RingClipToolOptions,RingAttachmentOption, andRingMaxAttachmentBytesOption.
RelatedDirect link to Related
- Ring integration guide: Ring Developer Console setup, account linking, and examples
- SignalProvider: The base class
RingSignalsextends - Notifications: Notification priorities, coalescing, and delivery