WendyOS Docs
Detailed referenceCloud

Notifications API

The NotificationService gRPC service creates, lists, and manages operator-facing Wendy Notifications in a Wendy Cloud organization.

Notifications API

The NotificationService gRPC service creates, lists, and manages operator-facing Wendy Notifications in a Wendy Cloud organization.

Proto package

wendycloud.v1 — defined in Proto/cloud/notifications.proto.

App-facing API (wendy.system.v1)

Apps with the notifications entitlement call wendy.system.v1.NotificationService over the Unix socket at $WENDY_SYSTEM_SOCKET (/run/wendy/system/system.sock). This creates a canonical Wendy Notification in the recipients' Companion inboxes. Cloud then attempts APNs delivery; apps do not send an arbitrary APNs payload directly.

WendyKit is currently the only application SDK for this API, and it is Swift-only. Apps written in other languages call this gRPC service directly. See Send notifications from a device app for entitlement and Cloud grant setup, Swift and direct gRPC examples, and delivery behavior.

The private socket binds every call to trusted app identity. The request cannot supply an app ID, device ID, or organization ID; the agent adds app identity and Cloud derives device and organization identity from device mTLS.

Send

Send(SendRequest) → SendResponse

The agent forwards the request to Cloud. notification_id is the caller-chosen resource identity, not a retry token. After successful creation, every reuse of its canonical UUID—including an otherwise identical request or a differently cased spelling—returns ALREADY_EXISTS; the prior success is never replayed.

Local validation and rate-limit failures happen before forwarding and do not claim the UUID. After correcting the request or waiting for the local rate limit, the caller may retry with the same notification_id.

SendRequest

FieldTypeDescription
audienceNotificationAudienceUnion of the user, organization team, and organization role selectors below.
titlestringNotification title.
bodystringNotification body.
severityNotificationSeverityINFO, WARNING, ERROR, or CRITICAL.
deep_linkstringAbsolute wendy:// URI.
notification_idstringCaller-chosen Notification resource UUID v4. Cloud returns canonical lowercase; any canonical reuse after creation returns ALREADY_EXISTS.
metadataoptional StructStructured JSON-compatible metadata.

NotificationAudience

The app-facing and Cloud messages use the same plural selector shape. All three fields have union semantics. At most 100 selector entries may be supplied across the three lists. Cloud normalizes and deduplicates them, remains authoritative for recipient resolution, and resolves at most 100 recipients for a device-app send.

FieldTypeDescription
user_idsrepeated stringUser IDs to include.
team_idsrepeated int32Organization team IDs to include.
rolesrepeated OrganizationRoleOrganization roles to include.

At least one selector is required. A user selected through more than one field receives one Notification.

SendResponse

FieldTypeDescription
notification_idstringCanonical lowercase UUID of the newly created Notification resource.

Recipient totals are intentionally omitted because team and role counts can disclose organization membership.

Device-app sources may create 10 accepted Notifications per minute, and the agent also smooths bursts locally. Repeated rate-limit violations can quarantine that app/device source for 15 minutes. Device-originated deep links are restricted to the source device; wendy://devices/current/live is the portable form. For device-originated APNs alerts, Cloud uses Wendy · <Cloud app name> as the banner title while retaining the supplied title on the stored Notification.

Notification message

Fields 1–8 are the stable legacy Companion wire contract. V2 adds canonical identity, content, audience, and created_by attribution without changing those legacy fields.

FieldTypeDescription
idint32Legacy numeric ID.
user_idstringRecipient user ID in legacy and per-user read views.
organization_idint32Organization ID.
bodystringNotification body.
severityNotificationSeveritySeverity level.
related_entitiesStructLegacy structured context.
created_atTimestampCreation time.
deleted_atoptional TimestampSoft-deletion time.
titlestringNotification title.
deep_linkstringAbsolute wendy:// URI.
notification_idoptional stringCanonical caller-chosen resource UUID v4; absent on legacy records.
metadataStructStructured JSON-compatible metadata.
audienceoptional NotificationAudienceOriginal normalized selector union.
created_by_user_idoptional stringAuthenticated user that created the Notification.
created_by_asset_idoptional int32Authenticated device asset that created the Notification.
created_by_app_idoptional stringTrusted app identity stamped by the Wendy agent.

Cloud API methods

CreateNotification (legacy)

CreateNotification(CreateNotificationRequest) → Notification

Creates a Notification for one user. This RPC and CreateNotificationRequest are deprecated in their protobuf descriptors and remain intact for existing Dashboard and legacy clients. wendycloud.v2 is no longer hypothetical — it is vendored at Proto/wendycloud/v2/ and generated into go/proto/gen/cloudpb/v2/ — and this RPC is still carried there, still marked deprecated. The migration marker records that it is to be removed from v2, not that it already has been.


CreateNotificationV2

CreateNotificationV2(CreateNotificationV2Request) → CreateNotificationV2Response

Claims the caller-chosen canonical UUID and creates one Notification resource for the resolved recipient union. The first canonical UUID use may succeed; every later use returns ALREADY_EXISTS, regardless of whether request content is identical or changed. Cloud does not replay the original response or send pushes a second time.

User-authenticated callers provide organization_id. Provisioned-device callers omit it because Cloud derives the organization and device from their certificate; the Wendy agent stamps app_id from trusted container state.

CreateNotificationV2Request

FieldTypeDescription
organization_idoptional int32Required for user-authenticated callers; omitted by provisioned devices.
audienceNotificationAudienceUnion of repeated user_ids, team_ids, and roles; see above.
titlestringNotification title.
bodystringNotification body.
severityNotificationSeverityINFO, WARNING, ERROR, or CRITICAL.
deep_linkstringAbsolute wendy:// URI.
notification_idstringCaller-chosen Notification resource UUID v4; Cloud stores and returns canonical lowercase form and rejects every canonical reuse with ALREADY_EXISTS.
metadataoptional StructStructured JSON-compatible metadata.
app_idoptional stringRequired for provisioned-device calls and stamped from trusted app identity by the Wendy agent.

CreateNotificationV2Response

FieldTypeDescription
notification_idstringCanonical lowercase UUID of the newly created Notification resource.

Recipient totals are intentionally omitted because team and role counts can disclose organization membership.


ListNotifications (server-streaming)

ListNotifications(ListNotificationsRequest) → stream ListNotificationsResponse

Returns one Notification per streamed message.

ListNotificationsRequest

FieldTypeDescription
organization_idint32Organization to query.
user_idstringUser whose Notifications are requested.
offsetoptional int32Number of matching Notifications to skip.
limitoptional int32Maximum number of Notifications to return.
severity_filteroptional NotificationSeverityRestrict results by severity.
include_deletedboolInclude soft-deleted Notifications.

ListNotificationsResponse

FieldTypeDescription
notificationNotificationNotification carried by this stream message.
totalint32Total number of matching Notifications.

Migration note: ListNotifications previously returned one unary response containing repeated notifications, next_page_token, and total_count. Clients must now receive the server stream until it closes and use offset/limit instead of page_size/page_token.


GetNotification

GetNotification(GetNotificationRequest) → Notification

Returns one Notification by ID.

DeleteNotification

DeleteNotification(DeleteNotificationRequest) → DeleteNotificationResponse

Deletes one Notification by ID.

MarkAsRead

MarkAsRead(MarkAsReadRequest) → MarkAsReadResponse

Marks the requested Notification IDs as read and returns the number marked.

Campaign notifications and catalog registration

A campaign with notify.on: event and notify.event: <name> sends an immediate notification for the matching event. notify.on: detection sends on an inference appearance. Omit notify.webhook for Wendy Cloud delivery, or supply an HTTP(S) endpoint for webhook delivery. episode_committed remains manifest intent for notification after upload; it is separate from immediate delivery.

Cloud delivery currently requires a legacy enrollment with positive numeric organization and asset IDs. Experimental direct-PKI/OIDC enrollments with only a SPIFFE principal cannot use this sender: it rejects them before sending with FailedPrecondition, reported in inference_status.notification_error. Use an explicit notify.webhook for immediate notifications on those devices.

The agent uses its enrolled device credentials and a bounded, nonpersistent queue. There are at most three attempts, with 10-second timeouts and the same event UUID. InvalidArgument, Unauthenticated, PermissionDenied, FailedPrecondition, AlreadyExists, Unimplemented, and DataLoss stop retries. A mismatched response notification ID is DataLoss. Other failures are retried with backoff. Errors appear in agent logs and campaign inference_status.notification_error.

App catalog API

The v1 AppService exposes UpsertApp, GetApp, UpdateApp, DeleteApp, and ListApps. It replaces the former CreateApp RPC; clients must regenerate against Proto/cloud/apps.proto. Mutations and GetApp carry both id and organization_id. UpsertApp accepts optional name and details; UpdateApp also accepts the owner/admin-controlled can_send_notifications grant. ListApps uses organization_id, optional offset, limit, and filter, returning apps and total. Pagination is offset-based, not page-token-based. Deployment registration uses GetApp first and UpsertApp only when absent, so it preserves existing metadata and never automatically grants notifications.

The vendored AppService contract matches Cloud's current v1 server. Earlier Companion schemas used different tags for update fields and page-token pagination, so those clients must be rebuilt with the current schema. The coordinated client ports are Companion SDK #1 and Companion iOS #223. The shared wire fixtures cover organization scoping, metadata updates, notification grants and offset pagination. These source changes do not upgrade installed clients; release the updated clients against the matching Cloud v1 contract. They do not change Cloud's separate v2 API.

On this page