# Notify — Make Notify part of your workflow Notify documentation. HTML version: https://notify.belweave.ai/docs ## Get connected Connect the source and the people who should receive its updates. Start with an account and device, then choose a webhook, an MCP client, or the CLI. ### Choose your connection Use Notify when a script, service, or agent needs to reach a person away from a terminal. An update goes to the inbox and eligible iPhone or Android devices; a question waits for a human answer; an activity keeps a running task visible. Choose a webhook for an existing system that can POST JSON. Choose [MCP](/docs/quickstart#mcp-connect) for a compatible agent client, or the [CLI](/docs/quickstart#cli-install) for terminal workflows. Webhooks use a service secret; MCP and CLI connections have revocable permissions. Personal and Family plans are free. ### Account, device, and service 1. Sign in to [Notify](https://notify.belweave.ai) with Apple or Google. Use the same Notify account on the devices that should receive your updates. 2. Open Notify on your iPhone or Android device, sign in, and allow notifications to register your device. 3. In the dashboard, add a service for one source, such as Warehouse scanner. Set its sender title, optional image, and destination URL. 4. Save the returned webhook URL in the sending system’s secret store. You can now [send an update](/docs/send-update#first-notification). For help with your account or setup, contact [info@belweave.com](mailto:info@belweave.com). ### Save the service credential The URL includes a bearer credential for this service. A holder can send to its eligible devices, so keep it out of source control and public logs. Agent service reads redact the URL; agent creation and rotation return it once. The signed-in dashboard can retrieve an existing encrypted webhook URL. Webhook URL: ```text https://notify.belweave.ai/hooks/whk_your_token ``` Rotation invalidates the previous URL. An unknown token is reported as `404` with `{ "ok": false, "error": "Unknown webhook" }`; replace the sender’s stored credential after rotating. ### Connect an MCP client For an MCP-capable client, configure Notify as a remote Streamable HTTP server. Use the shared endpoint below, then complete the browser consent flow when the client connects. MCP server URL: ```text https://notify.belweave.ai/mcp ``` OpenCode configuration belongs in `opencode.json`: ```json { "$schema": "https://opencode.ai/config.json", "mcp": { "notify": { "type": "remote", "url": "https://notify.belweave.ai/mcp" } } } ``` For Claude Code, use the HTTP add command below. Cursor’s configuration goes in `~/.cursor/mcp.json`: ```bash claude mcp add --transport http notify https://notify.belweave.ai/mcp ``` ```json { "mcpServers": { "notify": { "url": "https://notify.belweave.ai/mcp" } } } ``` ### Set up the CLI Download the versioned CLI bundle from [Notify Tools releases](https://github.com/Belweave/notify-tools/releases). Requires Node.js 22.13 or newer; no npm package installation is needed. Verify the download, inspect the installer, and install the exact release: ```bash curl -fLO https://github.com/Belweave/notify-tools/releases/download/v0.11.1/notify-tools-0.11.1.tar.gz curl -fLO https://github.com/Belweave/notify-tools/releases/download/v0.11.1/SHA256SUMS shasum -a 256 -c SHA256SUMS mkdir notify-tools-0.11.1 tar -xzf notify-tools-0.11.1.tar.gz -C notify-tools-0.11.1 cd notify-tools-0.11.1 node install.mjs --dry-run node install.mjs export PATH="$HOME/.local/bin:$PATH" notify auth login notify --help ``` 1. Run `notify auth login` to obtain a short device-authorization code and open Notify in your browser. 2. Sign in, inspect the requested scopes, and authorize the connection. 3. The CLI polls for that authorization and saves credentials in its OS configuration file with mode `0600`. Check your connection with `notify auth status`. Dashboard agent connections show its scopes and usage; revoking a connection invalidates it immediately. `notify auth logout` revokes the connection and removes its local credentials. Repeat `--scope` to request only needed permissions. `--client-name` labels the connection and `--expires-in` controls its lifetime (default `90d`). `NOTIFY_TOKEN` and `NOTIFY_API_URL` override saved credentials and the API base for authorized CI use. Keep credential values in your environment’s secret store. ## Send an update Send a useful message, file it into the inbox, and give the recipient a clear next step. Webhook and terminal examples below describe the same package-pickup workflow. ### Send from a webhook For a package-pickup alert, send a JSON object with `body`. The service supplies the sender title and other defaults. This example uses a placeholder token; substitute the URL saved during [connection setup](/docs/quickstart#webhook-url). ```bash curl -X POST https://notify.belweave.ai/hooks/whk_your_token -H 'Content-Type: application/json' -d '{"body":"Package PKG-42 is ready at the front desk."}' ``` ### Send from a terminal Use `notify send ` for an update from a terminal. It targets active supported devices unless you specify routing. Set the sender title per call; sends with the same title are threaded together. ```bash notify send "Package PKG-42 is ready" \ --title "Warehouse desk" \ --image https://warehouse.example.com/icon.png \ --url https://warehouse.example.com/packages/PKG-42 ``` | Flag | Type | Description | | --- | --- | --- | | `--title` | string | Sender name. Defaults to `Notify`. | | `--image` | url | Public HTTPS avatar, same rules as the webhook `imageUrl`. | | `--url` | url | Web URL, app deep link, or Shortcuts URL opened when tapped. | | `--device` | id, repeatable | Target eligible devices by stable ID. Included for free. | | `--project` | string | File the notification into a named project in the Notify app inbox. | | `--summary` | string | Short push/preview text for a long body. Bodies can hold up to 8,000 characters. | | `--markdown` | boolean | Record the body as Markdown (same as `--body-format markdown`); V1 renders plain text. | | `--app` | app id | Open this [web app](https://notify.belweave.ai/docs#web-apps) in Notify when tapped; `--url` must stay on its origin. | | `--idempotency-key` | string | Safe retries: replays return the original result without a second push. | | `--stdin` | boolean | Merge a JSON object from stdin under the explicit flags. | > A connection’s sends use the same rate/usage accounting as webhook sends: a requester window and an account-wide window. Notifications have no monthly cap. ### Organize longer messages Use `project` to keep recurring updates together, for example Warehouse maintenance. Names are matched within an account after Unicode normalization and case folding: `Warehouse maintenance` and `warehouse maintenance` resolve to one project, with the first spelling kept for display. Unfiled messages appear under Other. Put the full report in `body` and a short actionable digest in `summary`. The digest supplies banners and list previews; the complete body remains in the inbox detail. Clients currently show plain text and tappable links. `bodyFormat: "markdown"` stores the format metadata but does not enable Markdown rendering in V1. > The account limit is 500 projects. A send that introduces a name beyond this limit can still create the notification, unfiled, with a `message` explaining the fallback. Existing names continue to resolve. ### Give the tap a destination A destination belongs in `url`, either on the service or in the individual send. The recipient must tap to open it. A received push alone cannot launch a site, another app, or a shortcut. - Prefer a universal `https://` link when the destination supports app and browser routes. On iOS, an installed associated app can handle it; otherwise the website opens. - A documented custom scheme can address an installed app directly, such as `warehouse://packages/PKG-42`. Without a handler, the destination cannot open. - URL-encode spaces and reserved characters in route names and query values. Shortcuts are an iOS-specific destination. ```json { "body": "Package PKG-42 is ready for collection.", "url": "warehouse://packages/PKG-42" } ``` For an iPhone shortcut, follow Apple’s [run-shortcut URL format](https://support.apple.com/guide/shortcuts/run-a-shortcut-from-a-url-apd624386f42/ios). Match the saved shortcut’s name and encode its query values. `input=text` passes the `text` value; `input=clipboard` uses the device clipboard. ```json { "body": "Warehouse pickuped. Tap to run the follow-up.", "url": "shortcuts://run-shortcut?name=Package%20Pickup&input=text&text=collect%20PKG-42" } ``` > The device may ask the person to unlock or grant shortcut permissions. Notify cannot run a shortcut merely because a notification arrived. The server rejects executable or local schemes including `javascript:`, `data:`, `file:`, `blob:`, and `about:`. ## Ask for a decision Pause work for a person’s approval, choice, or reply. Creating a prompt is the beginning of that exchange; use its confirmed server outcome to decide what happens next. ### Ask and wait in the terminal Use `notify send ask ` when later work depends on an answer. Select one of `--approval`, `--yes-no`, or `--text`; ordinary send appearance flags also apply. The example asks whether a package should be held for collection. ```bash notify send ask "Hold package PKG-42 until tomorrow?" \ --approval --title "Warehouse desk" --wait --timeout 15m ``` For approval or yes/no, `--live-activity` requests an iOS interactive card (iOS 17+) with `approval`, `shell`, `verdict`, or `signal` style. Native Android receives an ordinary actionable alert for this presentation. The prompt is limited to 240 characters, labels to 1–24 characters, and expiry to eight hours. This presentation accepts no text replies, images, or URLs. Custom primary/secondary labels change displayed verbs; returned actions remain canonical. ```bash notify send ask "Send the pickup reminder?" \ --approval --live-activity --style signal \ --primary-label Send --secondary-label Deny \ --wait --timeout 15m ``` `--wait` waits for a terminal answer, subject to `--timeout` (by default, the requested prompt expiry: 15 minutes unless changed). `--poll` waits up to 20 seconds. Either wait may time out while the prompt stays pending; neither cancels it. Prompt expiry defaults to 15 minutes and is set separately with `--expires-in`. Resume with `notify interaction wait `; that standalone command defaults to a 60-second wait. A pending `interaction get` can exit successfully, so do not treat a successful read as approval. ```json { "interaction": { "id": "int_7MFuml-SqoUmpPLo", "kind": "reply", "status": "replied", "response": "Leave it at the desk", "respondedAt": "2026-10-12T11:54:17.927Z" }, "accepted": 1, "timedOut": false } ``` ### Ask from a webhook For a webhook question, include `response` next to the message. Choose `approval` for Approve/Deny, `yes_no` for Yes/No, or `text` for a typed reply. The warehouse example asks whether a delayed parcel should be held. ```json { "title": "Warehouse pickup", "body": "Hold package PKG-42 for collection?", "response": { "type": "approval", "expiresInSeconds": 900, "correlationId": "pickup-42", "callback": { "url": "https://warehouse.example.com/notify-answer", "token": "callback_token_placeholder_32_chars" } } } ``` | Field | Type | Description | | --- | --- | --- | | `type` | string, required | Required choice of `approval`, `yes_no`, or `text`. | | `expiresInSeconds` | integer | Answer window: integer from 30 to 86,400 seconds; default 900. | | `correlationId` | string | Optional sender-defined correlation value, maximum 100 characters, echoed in the callback. | | `callback.url` | string | Classic answered-callback destination. Requires public HTTPS and a paired callback token. | | `callback.token` | string | Classic callback bearer credential and HMAC key, 16–512 characters. | | `callback.target` | object | Connector destination `{ connectorId, route, path }`; use instead of callback.url/token. See the connector receiver guide. | | `webhookId` | string | Account-owned saved reply webhook ID (`whk_…`) selected for this interaction, in addition to applicable service/account defaults. | A successful question send adds pending-response metadata. Store the event ID and deadline; acceptance of the push does not authorize the requested action. ```json { "ok": true, "eventId": "evt_example_pickup", "delivered": 1, "response": { "status": "pending", "expiresAt": "2026-10-11T18:19:04.000Z" } } ``` ### Read or cancel the outcome Read the saved event ID to find a webhook question’s current outcome. To stop accepting answers before its deadline, POST to `/cancel`. Canceling the question and withdrawing the displayed notification are separate operations. ```bash curl https://notify.belweave.ai/hooks/whk_your_token/events/evt_example_pickup curl -X POST https://notify.belweave.ai/hooks/whk_your_token/events/evt_example_pickup/cancel ``` ```json { "ok": true, "event": { "id": "evt_example_pickup", "response": { "status": "approved", "action": "approve", "text": null, "correlationId": "pickup-42", "respondedAt": "2026-10-11T18:06:52.000Z", "expiresAt": "2026-10-11T18:19:04.000Z" } } } ``` - `pending` means no settled answer. Terminal statuses are `approved`, `denied`, `yes`, `no`, `replied`, `expired`, and `canceled`. - A text answer has `action: "reply"` and a populated `text`; choice answers carry the selected canonical action and `text: null`. - Reading an overdue pending question settles it as `expired`. - Only pending responses can be canceled; other states return `404` from cancel. The webhook can only inspect events belonging to its service. > An offline client may queue an answer locally. Queued is not server-confirmed: another valid answer, expiry, cancellation, or removal of access can win before synchronization. Continue automation only after the server reports the required terminal outcome. ### Route agent permission requests Permission hooks let Claude Code, Codex, and OpenCode V1/V2 pause for a person’s decision. A grant requires an explicit, valid approval and applies once. Denial, timeout, invalid input, authentication or network errors, and no accepted or queued device delivery all deny the request. Queued delivery keeps waiting; only a server-confirmed explicit approval grants permission. ```bash notify permissions setup all notify permissions doctor ``` Keep `notifications:send`, `interactions:create`, and `interactions:read` on the connection. The default login includes them; narrowed logins must request them. Setup and `permissions doctor` check scopes before installing hooks. Install one integration with `permissions setup claude`, `codex`, or `opencode`. Codex also requires review/trust in `/hooks`. On macOS, OpenCode setup installs its V1 plugin and V2 background connector. `notify permissions uninstall all` removes Notify-owned hooks and services. The phone receives a limited request summary: agent name, permission/tool name, project-directory basename, and resource count. It does not receive raw commands, patches, prompts, file contents, URLs, environment values, transcript paths, or absolute paths. ## Track work Keep one task’s current stage visible as work progresses. iOS uses ActivityKit on capable builds; native Android uses an ordinary ongoing progress notification through the same API. ### Begin a task | Route | Purpose | | --- | --- | | `POST /hooks/:token/live-activities` | Start an activity. Returns 201. | | `GET /hooks/:token/live-activities/:id` | Read the current state. | | `PATCH /hooks/:token/live-activities/:id` | Apply a partial update. | | `POST /hooks/:token/live-activities/:id/end` | Settle and dismiss the activity. | Start one activity for a task that has several meaningful stages. `title` identifies the task and `status` describes its current stage; both are required. Here a warehouse pickup begins preparation. ```bash curl -X POST https://notify.belweave.ai/hooks/whk_your_token/live-activities \ -H 'Content-Type: application/json' \ -H 'Idempotency-Key: pickup-42-start' \ -d '{ "title": "Pickup PKG-42", "status": "Preparing", "subtitle": "Pick and pack", "detail": "Warehouse team is gathering the parcel", "progress": 0, "symbol": "build", "accentColor": "#FF9F0A" }' ``` ```json { "ok": true, "activityId": "act_example_pickup", "sequence": 0, "status": "active", "accepted": 1, "failed": 0, "state": { "title": "Pickup PKG-42", "status": "Preparing", "progress": 0, "symbol": "build" }, "expiresAt": "2026-10-12T02:04:11.000Z", "staleAt": "2026-10-11T22:04:11.000Z", "endedAt": null } ``` Save `activityId` for subsequent writes. Alternatively, set a start `key` and use that alias in the URL. Start, update, and end accept service-scoped `Idempotency-Key` headers: a matching replay returns `idempotent: true`; a changed payload with the same key returns `409`. > On iOS, a capable Notify build must register a push-to-start token. Native Android uses an ordinary ongoing progress notification through FCM on these same routes. A `201` with `accepted: 0` and a `message` means no eligible target accepted the start. Neither provider acceptance nor a successful response proves that a card is visible. ### Report the next stage Update the existing task with `PATCH` instead of starting another one for each stage. Omitted fields keep their current values, and a committed update increments `sequence`. Include at least one changed field besides `ifSequence`. ```bash curl -X PATCH https://notify.belweave.ai/hooks/whk_your_token/live-activities/act_example_pickup \ -H 'Content-Type: application/json' \ -d '{ "status": "Packing", "progress": 0.6, "accentColor": "#64D2FF" }' ``` - Clear an optional `subtitle`, `detail`, or `progress` value by sending `null`. - For concurrent writers, send the last observed `ifSequence`. `409 Sequence conflict` includes the current state so the writer can reconcile. - An ended or expired activity rejects updates with `409 Live Activity is already terminal`. ### Finish the task ```bash curl -X POST https://notify.belweave.ai/hooks/whk_your_token/live-activities/act_example_pickup/end \ -H 'Content-Type: application/json' \ -d '{ "status": "Collected", "progress": 1, "symbol": "success" }' ``` End the activity when the pickup completes or stops. You may omit the body. On iOS, `dismissAfterSeconds` sets the finished card’s remaining visibility: 0–14,400 seconds, default 0. An iOS start reports its update token asynchronously. A provider-accepted start remains `starting` until registration. Fresh starts share a durable 60-second interval per device across all agents, services, and interactive prompts; additional starts return `queued`. This precaution does not guarantee display. Updates and ends also queue while a token is missing. When delivery becomes possible, only the newest contiguous routine progress snapshot is sent; starts, interaction changes, and endings are preserved. `superseded` counts skipped snapshots without claiming delivery. End marks the logical activity ended immediately. Recovery runs every 15 seconds and work expires at its original deadline. `accepted`, `failed`, `queued`, and `superseded` are disjoint operation outcomes. Transient rejection stays queued. None of these counts alone confirms device display. > The end route supplies `status: "Complete"` and `symbol: "success"` when omitted, replacing the previous values. Set them explicitly for a different final message. ### Track from a terminal The CLI can start, update, inspect, and end the same activity lifecycle without a webhook. Use the returned ID or a start `--key`; select an iOS progress layout with `--style`. The example uses one key for a warehouse preparation task. ```bash notify activity start --key pickup --replace --style ring \ --title "Pickup PKG-42" --status "Preparing" --progress 0.1 notify activity update pickup --status "Packing" --progress 0.6 --if-sequence 0 notify activity end pickup --status "Collected" --progress 1 --dismiss-after 45s ``` - `--replace` deliberately ends activities occupying the target slot or key before starting this one. - `--if-sequence` applies the write only at the expected sequence; reconcile a conflict before retrying. - Progress styles are `standard`, `ring`, `hero`, `terminal`, and `steps`. An update can change the style. - Inspect one activity with `activity get `, or recent activity records with `activity list`. ## Bring a web app Make your warehouse board or household planner available inside Notify. Register its origin and send updates that open the relevant page. Add optional microAuth() when your site needs sign-in. ### Add your site Bring an HTTPS site you control into Notify using `notify apps create`. A repeated registration of the same URL updates its name, icon, or project. Ordinary websites open without sign-in. An integrated site can explicitly request microAuth(); the viewer then approves sign-in and chooses any profile details to share. New sharing choices start off. Manage the profile in Settings → microAuth profile and per-app grants in Access & details. ```bash notify apps create --name "Warehouse board" --url https://warehouse.example.com --icon https://warehouse.example.com/icon.png notify apps list notify apps remove app_example_warehouse ``` Use an HTTPS launch URL. Only `localhost` development URLs may use plain HTTP. Each account can register up to 100 apps. Agent management requires `apps:read`/`apps:write`; reauthorize if these scopes are missing. The iOS hosted view requires iOS 18.4+. It supports standard web inputs for camera, microphone, location, motion, and files. iOS builds with device features additionally expose Promise-based `window.notify.haptics(style)`, `authenticate(reason)`, `authenticateChallenge(challenge)`, `speech.start()`/`speech.stop()`, `share(data)`, `openSettings()`, and `capabilities()`. Pocket-friendly optional helpers on both platforms include `haptic(style)` with light, success, or warning, no-argument `authenticate(): Promise` for local biometrics without passcode fallback, and `dictate(): Promise` for native speech. Passing a reason to `authenticate(reason)` retains the detailed result object. Keep keyboard input available and handle permission denial or recognition errors. Local Face ID does not require passkey domain association. Native Android builds with device features expose `getToken()`, `close()`, `capabilities()`, `haptics()` (also `haptic()`), `authenticate()`, `speech.start()`/`speech.stop()`, `share()`, and `openSettings()`. Android authentication and speech use an app-name and exact-origin Allow once dialog for each request. Android does not provide signed `authenticateChallenge` proofs, file/download, location, or inline-media integrations. Android passkeys are limited to `https://pocket.belweave.ai`, API 28+, and supported WebKit credential integration; they require Pocket Digital Asset Links association with the actual signed app certificate. A true capability flag does not verify that association; iOS passkeys require AASA and Associated Domains. Neither association follows automatically from registering a hosted app. Feature-detect for platform/build/browser differences. Review the app-level grant and OS permission separately. On iOS before 27, blocking the Notify location wrapper cannot fully gate raw WebKit geolocation; set Location to Never in system settings for a complete OS-level revocation. Local biometric authentication is an owner check, not server identity proof; a signed challenge requires your service to bind the key and validate a fresh nonce. > On iOS, Settings → App device permissions stores Ask/Allow/Block per account, app, and origin. Allow does not bypass OS denial, and Ask again does not reset OS/WebKit grants. On iOS versions before 27, raw WebKit location calls can bypass the wrapper’s revocation; disable Notify’s global Location permission to fully stop that access. A local biometric check is not server identity. Server authorization needs a verified pass, verified WebAuthn assertion, or an enrolled and verified challenge proof. ### Establish the viewer’s identity For new integrations, download the SDK from [Notify Tools](https://github.com/Belweave/notify-tools) and copy the release’s complete `sdk` directory into your project as `vendor/notify`. Import browser helpers from `./vendor/notify/browser.mjs`. Feature-detect `supportsMicroAuth()` and call `connectMicroAuth({ challengePath, sessionPath, fields })` from your site’s sign-in control. Your server uses `createMicroAuthServer` from `./vendor/notify/server.mjs` with a durable challenge store. The SDK verifies the signed pass against a fresh, single-use challenge bound to an HttpOnly browser cookie, then your server creates its own session. Current microAuth support targets iOS bridge version 3; keep ordinary browser and older-client sign-in available. Optional profile fields are name, email, phone, timeZone, pronouns, and custom text. The viewer sees actual values and chooses each custom detail separately. The app receives only requested and approved values. Profile values are self-described; email is the Notify account email, not an independent verification claim. Revoking access stops future passes but does not end the website’s existing sessions or erase copies it already received. 1. For legacy integrations, feature-detect the hosted bridge, then call `await window.notify.getToken()` to obtain the short-lived pass. `window.notify.close()` exits the hosted view. 2. Send the pass to your backend. Verify the ES256 JWT against [Notify’s JWKS](https://notify.belweave.ai/.well-known/jwks.json), using a JWT verifier such as `jose.jwtVerify`. 3. Require issuer `https://notify.belweave.ai`, your exact app origin as audience, protected header `typ: "notify-pass+jwt"`, and algorithm `ES256`. Check expiry and reject replayed `jti` values before creating your own session. | Field | Type | Description | | --- | --- | --- | | `sub` | string | Pairwise user ID (`hk_…`) scoped to your origin. Different origins receive different subjects; apps registered on the same origin share this subject. | | `aud` | string | Your app's origin, e.g. `https://app.example.com`. | | `iat / exp` | number | Passes expire two minutes after issue. | | `jti` | string | Unique per pass; reject reused values to block replay. | | `app_id` | string | The Notify app ID the pass was issued for. | | `name / email` | string | Present only when the owner shares them. New sharing choices start off. microAuth() also limits the pass to the fields the site requested. | | `team_id / team_role` | string | Present for [team apps](/docs/teams#teams-apps): the viewer's team and their current role (`owner`, `admin`, or `member`). | > Exchange the verified pass for your server’s session; do not store the two-minute pass as a permanent login credential. The native Android app’s browser-PKCE device sign-in is a separate protocol from this hosted-site pass. ### Open the right page When an update belongs to a hosted app, attach its `appId` (CLI: `--app`). Tapping opens that app. An optional `url` points to a page within the registered origin; cross-origin destinations are rejected. ```json { "body": "The pickup schedule is ready", "appId": "app_example_warehouse", "url": "https://warehouse.example.com/reports/latest" } ``` ## Share with family Coordinate household alerts without sharing an account or device. A free family has one owner and up to three additional members, with shared services and unlimited notifications. ### Invite the household A household can share a Family account group while each person keeps their own sign-in. Create it through the signed-in `POST /api/teams` route, or `POST /api/agent/teams` with the required scope, using `kind: "family"`. An account can join only one family. ```json {"name":"Our family","kind":"family"} ``` ```bash notify families create "Our family" notify families invite family_id --email person@example.com notify families list ``` Replace `family_id` with the returned ID. IDs retain the `team_…` prefix; check `kind` to identify a family. Inspect people with `notify families get`, `members`, and `invites`. Owners may `rename`, `remove-member`, or `revoke-invite`; members may `leave`. The owner has three additional places. Pending invitations reserve a place until they expire or are revoked; trying to exceed capacity returns `409`. Invitees join as `member`. Families have no admin role or domain-restricted invitations. The recipient opens the invite link and signs in on the website. `POST /api/team-invites/:code/accept` requires a human session; an email-addressed invitation is restricted to that account. ### Share services and apps A shared service can collect a household answer. Any accepted family member using a supported interactive client may respond, and the first valid server-confirmed answer settles the prompt. `respondingUserId` identifies that person. Unaccepted invitees, removed members, and outsiders cannot respond. The service creator retains its callback configuration and webhook credential. To share your service, POST `{ "familyId": "team_…" }` to `/api/services/:id/share` or `/api/agent/services/:id/share`. POST `{ "familyId": null }` to make it personal. The agent route needs `services:write`. ```bash notify services share service_id --family family_id notify services share service_id --personal ``` Members receive shared alerts and have their own read state for shared inbox history. The original creator controls sharing, token rotation, and webhook URL access. The family owner may rename or delete a shared service. Leaving the family returns the creator’s services to personal use. A family’s accepted members have unlimited notifications. Device targeting, interactive answers, callbacks, activities, and shared web apps remain included. Each member registers and manages their own devices. > Workplace-only routes return `403` for families: rotations, paging, device groups, member-device administration, domain verification, audit logs, retention, and team billing. For a family web app, POST `{ "teamId": "team_…" }` to `/api/apps/:appId/share` or `/api/agent/apps/:appId/share`. The app’s original contributor can also use `notify apps share app_id --team team_id`. List shared apps with `GET /api/teams/:familyId/apps`; every viewer approves app sign-in individually. ## Delivery and reliability Understand what a send proves, how retries behave, and how device availability affects an update. Use these rules when turning the guides into unattended workflows. ### Acceptance is not a receipt ```json { "ok": true, "eventId": "evt_example_pickup", "delivered": 1 } ``` Save `eventId` if you will inspect an answer or request withdrawal. `delivered` counts push requests accepted across the configured providers: Expo for iOS and FCM for native Android. It is not a delivery receipt, proof of display, read state, or a human answer. Notification detail responses now include `pushReceipts` for the reader’s iOS devices: `pending`, `accepted` by Apple, `error` with a safe provider code, or `unknown` when no receipt arrives within 24 hours. Checks begin after one minute and retry every five minutes; diagnostics are retained for 30 days. Older sends have no receipts. A receipt still cannot prove on-screen display. `DeviceNotRegistered` retires only the matching Expo token; a newer registration and Live Activity credentials are preserved. Interactive service questions persist their original push targets with the question before sending. While retries remain, HTTP `202` includes `queued: N` alongside the persisted `delivered` count. Repeat the identical payload and idempotency key to read that same event’s current counts. Retries preserve the interaction and response credential, recheck authorization and expiry, and never add newly registered devices. Exhausted delivery with no provider acceptance returns `502`. A provider request can repeat after an ambiguous network result; deduplicate by interaction ID. > With no eligible devices, the event can still be created with `delivered: 0` and an explanatory `message`. Decide explicitly whether your workflow requires an available device. For questions, proceed only after an approved or otherwise appropriate terminal answer. ### Retry without duplicate sends Give each logical send a stable `Idempotency-Key` when a network retry is possible. The header accepts 1–200 characters, and its namespace belongs to the service. Reuse a key only for an identical payload. - An already completed matching request returns its original event and `idempotent: true`. - A matching request still being processed returns `202 Accepted`; wait before checking again. - A key associated with a different payload returns `409 Conflict`. - An empty key or a key longer than 200 characters returns `400`. ### Choose recipients Omit targeting to use the account’s active supported iOS and native Android devices, ordered by most recent activity. Personal accounts support unlimited devices. Set `deviceIds` to 1–50 stable IDs from the dashboard when only selected devices should receive the update. A foreign or unknown selection returns `400 Invalid device selection`. Owned devices that are inactive or unsupported are skipped. Workplace `group` targeting uses a separate permission check. ```json { "body": "Package PKG-42 needs a pickup decision.", "deviceIds": ["dev_your_iphone_id"] } ``` Targeting has no paid personal-plan gate. Availability still depends on the device’s registration, platform capabilities, and operating-system notification settings. ### Remove an obsolete alert If a package alert becomes obsolete, use its saved `eventId` to request removal from the service owner’s active supported devices. Withdrawal also cancels an associated pending question. To stop answers without requesting alert removal, use [question cancellation](/docs/ask-decision#response-status). ```bash curl -X POST \ 'https://notify.belweave.ai/hooks/whk_your_token/events/evt_example_pickup/withdraw' ``` ```json { "ok": true, "eventId": "evt_example_pickup", "status": "withdrawn", "accepted": 1 } ``` > `accepted` counts provider acceptance of the removal command, not confirmed device removal. Background work may run late or never run, including after an iOS force-quit. Use the webhook belonging to the original event. Withdrawal commands target the owner’s devices, not every original family/group recipient; those recipients may retain their alert. A settled repeat returns the status with `accepted: 0` and `idempotent: true`, without another command. ### Rate limits and usage | Limit | Personal · free | Family | | --- | --- | --- | | Requests per minute, per service | 300 | 300 | | Requests per minute, per account | 1,500 | 1,500 | | Notifications per month | Unlimited | Unlimited | | Active devices | Unlimited | Unlimited | Hosted defaults are shown above. Notifications, questions, and activity operations share rolling 60-second counters. A per-minute rejection returns `429`, `Retry-After: 60`, and `retryAfterSeconds`. There is no monthly notification cap. Personal and Family include unlimited notifications and devices for free. Per-minute rate limits still apply to notifications, questions, and activity operations. Teams is TBD and is not currently purchasable. ### Expiry and stale content Set `expiresInSeconds` to the longest useful lifetime for the task: 60–28,800 seconds, with 28,800 as the default. After the deadline, state becomes `expired` and further updates are refused. `staleAfterSeconds` expresses how long an update should be trusted: 0–28,800 seconds, default 14,400. On iOS, stale content remains visible and updateable but is marked as potentially outdated. Each update moves the stale deadline forward without exceeding expiry; omitting this field retains the previous stale window. ### Activity slots and replacement Notify allows up to five concurrent activities per target device, including interactive activities. Distinct keys can coexist. Starting the same active key for the same service or token without replacement returns this `409` response: ```json { "ok": false, "error": "A Live Activity with this key is still active", "code": "ACTIVE_ACTIVITY_CONFLICT", "activityId": "act_example_pickup" } ``` Use `code` to identify a key conflict and update the existing task. Device capacity instead returns `422`, `code: ACTIVITY_CAPACITY_EXCEEDED`, and `limit: 5`; end one of your running tasks before starting another. Interactive Live Activity asks use the same rejection: if any target is full, the whole mixed-target request rolls back without a queued receipt or push. Retry the same payload and idempotency key after a slot is released. Five is Notify’s admission policy, not a hardware specification. A deliberate replacement uses the same `key` with `replace: true`. It ends only the matching activity owned by this service or token before the new start. `replaced` reports how many were displaced, or zero if no replacement was needed. Device removal remains subject to provider and OS behavior. Replacement preserves other keys and other requesters’ activities. The matching activity is ended everywhere, including outside the new target set, so its alias can be reused. A stable key plus replacement suits repeated runs of the same task. ### Activity delivery behavior On iOS, starts include an alert; update/end pushes are silent. Routine progress updates use APNs priority 5. Starts, endings, and interactive state use priority 10. Priority does not grant sound, haptics, or display. Android updates modify the same ongoing progress notification; OS settings still control presentation. Send updates at meaningful transitions. Activity operations share notification rate limits. There is no monthly allowance to exhaust. > Read `activity.readiness` to distinguish ready devices, queued starts and accepted starts awaiting an update token. After 90 seconds without registration, `update_token_delayed` is a diagnostic warning, not proof of Apple throttling; the activity still accepts late registration until its deadline. Open Notify and check Live Activity settings if it persists. Reuse one activity for each continuing task and send meaningful progress changes. Unlimited monthly usage does not mean unlimited delivery frequency. ## Access and consent Keep the connection’s permissions as small as the work requires. Agent grants authorize API operations; human actions and native-device permissions remain separate decisions. ### Agent credentials and scopes Agent HTTP requests use `Authorization: Bearer ntf_…`. Create a named, scoped key in [Dashboard → API keys](https://notify.belweave.ai/dashboard#api-keys), or use CLI device authorization. Missing route scopes return `403` with a `required` list. Resource access is constrained to the token’s account and permitted shared resources; foreign resource IDs return `404`. For a server integration, choose Create API key, give it a recognizable name, and review its scopes and expiry. The Server integration preset grants `notifications:send`, `interactions:create`, and `services:write`; the default expiry is 90 days. Copy the secret when it appears and save it in your server’s secret store: it is shown only once. Revoke a key from API keys when the integration no longer needs access. For terminal use, `notify auth login` remains the browser authorization flow. MCP clients can continue using OAuth at `https://notify.belweave.ai/mcp`; neither workflow requires manually creating a dashboard key. - The default CLI login requests all agent scopes except `events:read` and `tokens:manage`. Explicit `--scope` flags replace the default scope set; repeat the flag for each permission you need. - Older/narrower connections may lack device, inbox, billing, team, on-call, or connector scopes. Compare `auth status` with the reference’s route requirements and reauthorize only what you need. - Agent service list/get omit webhook URLs. Create/rotate reveal the URL once; signed-in dashboard access is separate. ### Actions reserved for a person A scoped connection can perform supported operational work, but it cannot make these identity or consent decisions. Use the signed-in website or native client where that human action is available: - Answer a question: only a human may approve, deny, choose yes/no, or submit a reply. Agents can read and cancel. - Create API tokens: minting a broader token would defeat the calling token’s scope/lifetime. `tokens:manage` permits listing and revoking existing tokens only. - Approve hosted-app sign-in, select profile sharing, or issue a Notify pass. Changing an app’s origin resets its approval. - Register a device through its native client and push-token flow. - Accept a family or workplace invitation. - Acknowledge or escalate a page. ## Webhook reference Exact notification routes, payload fields, HTTP outcomes, and receiver formats. Use the task guides for a first send or question; use this chapter when wiring a sender or callback receiver. ### Notification routes | Route | Purpose | | --- | --- | | `POST /hooks/:token` | Send a notification. | | `GET /hooks/:token/events/:eventId` | Read the state of an interactive response. | | `POST /hooks/:token/events/:eventId/cancel` | Withdraw a pending interactive response. | | `POST /hooks/:token/events/:eventId/withdraw` | Request removal of a delivered notification. | All webhook writes use JSON with `Content-Type: application/json`. The token identifies the owning service. Invalid JSON fields return `400` and an `issues` array; a token that does not resolve returns `404`. Keep event IDs from successful sends for the event-specific routes. ### Notification fields | Field | Type | Description | | --- | --- | --- | | `body` | string, required | Trimmed nonempty message: at most 8,000 UTF-16 code units and 16,384 UTF-8 bytes. Including `response` reduces the code-unit limit to 2,000. | | `title` | string | Optional sender title for this message; maximum 80 characters. Otherwise use the service title. | | `imageUrl` | string | Optional avatar, maximum 2,048 characters. Requires public HTTPS; local names and loopback, private, or link-local addresses are invalid. | | `url` | string | Tap destination, maximum 2,048 characters. Accepts web links, universal links, documented app schemes, and iOS Shortcuts URLs. | | `deviceIds` | string[] | Select 1–50 device IDs instead of the default active-device fanout. Exclusive with `group` and `oncall`. | | `group` | string | Workplace device-group ID (`dgrp_…`). Requires team membership and group targeting permission. Exclusive with `deviceIds` and `oncall`. | | `oncall` | string | Workplace rotation ID (`ocg_…`). Creates a page with a body up to 2,000 characters; exclusive with `deviceIds`, `group`, and `response`. Families cannot page. | | `response` | object | Question configuration: approval, yes/no, or typed answer. Exclusive with `oncall` and `appId`. | | `project` | string | Inbox project name, maximum 80 characters. The first matching send creates the project if capacity permits. | | `summary` | string | Banner/list digest, maximum 500 characters. The full message remains in the inbox. | | `bodyFormat` | enum | Stored format marker: `text` (default) or `markdown`. V1 clients still display plain text. | | `appId` | string | Registered hosted-app ID (`app_…`) available to the sender. Tap opens that app; an optional `url` must match its origin. Exclusive with `response`. | ### HTTP results ```json { "ok": true, "eventId": "evt_example_pickup", "delivered": 1 } ``` | Field | Type | Description | | --- | --- | --- | | `200` | ok | Accepted, or an idempotent replay. | | `202` | ok | An identical request is still processing. | | `400` | error | Invalid payload, key, or device selection. | | `404` | error | Unknown webhook token. | | `409` | error | Idempotency key reused with a new payload. | | `429` | error | Rate limit exceeded. | | `502` | error | All targeted push requests failed with the configured providers. | Provider diagnostics belong in the dashboard activity log because raw errors may contain device push credentials. Inspect that log when a provider rejects a target; the caller receives the public status shape instead of sensitive provider output. ### Per-question callbacks A per-question `callback` receives answered outcomes as JSON with user agent `Notify-Callbacks/1`. A classic `{ url, token }` callback uses a public HTTPS destination and `Authorization: Bearer `. A connector `{ target: { connectorId, route, path } }` resolves the active clawchest.com address when delivery runs. Both carry `Notify-Timestamp` and `Notify-Signature: v1=` over `{timestamp}.{rawBody}`. Redirects are refused and each request times out after 10 seconds. ```json { "type": "notification.response", "interactionId": "int_example_pickup", "actionDigest": "", "eventId": "evt_example_pickup", "correlationId": "pickup-42", "kind": "approval", "status": "approved", "action": "approve", "text": null, "respondedAt": "2026-10-11T18:06:52.000Z" } ``` The callback’s `kind` maps a `text` request to `reply`; `approval` and `yes_no` keep their names. Correlate and deduplicate answers using `interactionId`. The existing `eventId` and `correlationId` remain available for service notifications, but can be null for agent-created asks. `actionDigest` identifies the exact stored Notify prompt; it is not proof that an external action executed. Accept additional JSON fields when reading callbacks. The callback tries immediately and, on non-2xx failure, again after 30 seconds, 2 minutes, 10 minutes, and 1 hour. Five failed attempts exhaust delivery. Store an outcome durably before returning 2xx. > This callback only reports a person’s answer. It does not report expired or canceled questions. Poll the event route for those states, or use an account reply webhook for interaction lifecycle events. ### Account reply webhooks Use an account reply webhook when several tools should report interaction outcomes to one receiver. It subscribes to `interaction.answered`, `interaction.expired`, and/or `interaction.canceled`. Create a named HTTPS endpoint in the dashboard and enable it. Use for account replies makes it the single account default, including service-free MCP/API prompts. API and MCP updates use `isDefault: true`; `false` clears that endpoint’s default status. Selecting a new default replaces the old one. For each outcome, Notify considers the enabled account default, the interaction’s explicit `webhookId`, and the service’s current `defaultReplyWebhookId`. Each distinct subscribed endpoint receives one durable delivery record; repeated selection of the same endpoint does not duplicate that record. Network retries may still reach a receiver more than once. A service’s selected webhook must have the same owning account. 1. Select Generic HTTPS, grok, or Slack. New dashboard endpoints initially select answered, expired, and canceled outcomes; editing an existing endpoint preserves its selected outcomes. A grok routine supplies its webhook URL and Authorization header. Slack incoming webhooks receive a JSON `text` field along with delivery/signature headers. 2. For service-free MCP or API questions, enable Use for account replies on the endpoint and subscribe to the desired outcomes. This avoids attaching `webhookId` to each question. 3. Add receiver-specific custom headers if needed. Reserved security/transport headers cannot be replaced. Header values are encrypted and read back as `••••••••`; either preserve saved headers or replace the entire list with actual values, never the mask. 4. Store the one-time signing secret at the receiver. Rotation supplies a replacement and makes the old secret unusable; update the receiver accordingly. 5. Use Test & logs to queue an `interaction.answered` sample with `test: true` (`202` means queued, not delivered). Inspect up to 50 recent deliveries, including latency, response code, and saved attempt details. Older deliveries may lack per-attempt history. Manage these endpoints with MCP `webhooks_create`, `webhooks_list`, `webhooks_get`, `webhooks_update`, `webhooks_delete`, `webhooks_test`, and `webhooks_rotate_secret`, or the dashboard `/api/reply-webhooks` routes. Create and rotate return the signing secret once. Get includes the last 50 deliveries. A delivery’s `interactionId` links it to the originating ask; test deliveries use null. Older servers may omit that field. An account can have up to 25 endpoints. Agent endpoint reads require `services:read`; writes require `services:write`. Before parsing a request, verify `X-Notify-Signature: t=,v1=`. Sign the timestamp, a dot, and the unchanged body bytes with HMAC-SHA256 and the endpoint’s signing secret. Reject future timestamps and timestamps older than five minutes, then compare digests in constant time. Parsing and reserializing JSON cannot preserve this signature input. Node receiver helper; supply the original body bytes as a `Buffer`: ```javascript import { createHmac, timingSafeEqual } from "node:crypto"; export function verifyNotify(rawBody, signature, secret) { if (!Buffer.isBuffer(rawBody) || typeof signature !== "string") return false; const match = /^t=([0-9]+),v1=([a-f0-9]{64})$/.exec(signature); if (!match) return false; const [, timestamp, hex] = match; const now = Math.floor(Date.now() / 1000); const age = now - Number(timestamp); if (!Number.isSafeInteger(Number(timestamp)) || age < 0 || age > 300) return false; const expected = createHmac("sha256", secret) .update(timestamp + ".") .update(rawBody) .digest(); return timingSafeEqual(expected, Buffer.from(hex, "hex")); } // Use a raw-body middleware before any JSON parser: // verifyNotify(req.body, req.get("X-Notify-Signature"), process.env.NOTIFY_WEBHOOK_SECRET) // Only after verification: JSON.parse(req.body.toString("utf8")) ``` Python receiver helper; supply the original body bytes as `bytes`: ```python import hashlib import hmac import re import time def verify_notify(raw_body: bytes, signature: str, secret: str) -> bool: if not isinstance(raw_body, bytes) or not isinstance(signature, str): return False match = re.fullmatch(r"t=([0-9]+),v1=([a-f0-9]{64})", signature) if not match: return False timestamp, received = match.groups() age = int(time.time()) - int(timestamp) if age < 0 or age > 300: return False signed = timestamp.encode("ascii") + b"." + raw_body expected = hmac.new(secret.encode("utf-8"), signed, hashlib.sha256).hexdigest() return hmac.compare_digest(expected, received) # Read the raw body before calling the framework's JSON parser. # After verification, decode with json.loads(raw_body). ``` > Use durable deduplication after signature verification. `X-Notify-Delivery-Id` identifies a delivery across retries; commit its record together with the side effect. Already processed IDs should return 2xx. Retries preserve the ID and body but refresh the signature timestamp. Tests and separate lifecycle outcomes receive separate IDs. Reply webhooks try immediately and then after 1, 5, 15, and 40 minutes. Timeouts and 5xx responses retry; 4xx responses and redirects are final failures. Any 2xx acknowledges delivery. Disabling stops new deliveries. These endpoints can coexist with classic callbacks, but their payloads, signature headers, and retry schedules differ. ## Activity fields and layouts Field limits and platform-specific appearance for progress activities and interactive iOS cards. The lifecycle guide is [Track work](/docs/activity-api). ### State fields and iOS layouts | Field | Type | Description | | --- | --- | --- | | `title` | string, required | Task name, maximum 80 characters. Required at start; may be replaced by an update. | | `status` | string, required | Current stage, maximum 60 characters. Required at start. | | `subtitle` | string | Optional short second-line summary replacing visible status in expanded/Lock Screen layouts only, maximum 120 characters. Compact and small-banner views retain status. `null` clears it. Display budgets below are smaller than the accepted payload limit. | | `detail` | string | Optional supporting text, maximum 240 characters, shown in a two-line detail region. `null` clears it. Overflow remains bounded to the available layout. | | `progress` | number | Optional fraction from 0 through 1. `null` clears it. | | `symbol` | enum | Progress icon: `terminal` (default), `code`, `build`, `success`, or `warning`. | | `accentColor` | string | Six-digit hex, `#RRGGBB`. Defaults to `#246BEB`. | | `style` | enum | iOS progress layout: `standard` (default), `ring`, `hero`, `terminal`, or `steps`. Updates can select another layout; older builds may use the default. | | `privacyMode` | enum | `standard` or `private`. On iOS/APNs, private replaces start-alert text with a generic message; the activity content still includes its title/status. | | `key` | string, start only | Start-time alias, maximum 100 characters, accepted in place of the generated activity ID. Reusable after the previous activity becomes terminal. | | `replace` | boolean, start only | Start-time replacement flag (default false). Ends only the matching active key owned by the same requester. The response includes `replaced`. | | `deviceIds` | string[] | Start targeting list of 1–50 device IDs. Omit for eligible capable devices. | These are iOS layout choices, illustrated below. Progress layouts and interactive choice layouts serve different requests. Native Android shows ordinary notifications; it does not reproduce ActivityKit, Dynamic Island, or this layout gallery. These captures illustrate layout rather than proving push delivery. Use a short title for the task name, `status` for its current stage, and `subtitle` for a whole-word second-line summary when useful. Display budgets guide writing; they are not guaranteed fit or API rejection limits. Glyph widths, device size, locale, and text size change available space. The native layout measures text, can reduce it to 75% of its base size, then clips at a whole-word boundary if needed. Original text remains available to VoiceOver. Expanded Dynamic Island and Lock Screen layouts keep a static second-line summary. Its overflow continues before explicit `detail` in a two-line detail region; title overflow does not crowd that region. Compact views hide title, subtitle, and detail; they retain status with a suggested eight-character authoring budget when progress is absent, except terminal, which shows its prompt icon. Actual fit uses measured width rather than a fixed character cutoff. With progress, the percentage takes precedence. Detail can still exceed its two-line space; do not rely on every accepted character being visible. > A continuous marquee is not promised. [Apple limits widget and Live Activity animations to two seconds](https://developer.apple.com/documentation/widgetkit/animating-data-updates-in-widgets-and-live-activities), and Always-On presentation skips animations. Notify uses the static whole-word summary and two-line detail fallback, including with Reduce Motion enabled; this fallback does not animate. | Field | Type | Description | | --- | --- | --- | | `standard` | Compact | title 0; subtitle 0; status 8; detail 0 | | `standard` | Expanded / Lock Screen | title 32; subtitle 36; status 36; detail 100 | | `ring` | Compact | title 0; subtitle 0; status 8; detail 0 | | `ring` | Expanded / Lock Screen | title 32; subtitle 30; status 30; detail 80 | | `hero` | Compact | title 0; subtitle 0; status 8; detail 0 | | `hero` | Expanded / Lock Screen | title 24; subtitle 36; status 36; detail 100 | | `terminal` | Compact | title 0; subtitle 0; status 0; detail 0 | | `terminal` | Expanded / Lock Screen | title 36; subtitle 40; status 40; detail 100 | | `steps` | Compact | title 0; subtitle 0; status 8; detail 0 | | `steps` | Expanded / Lock Screen | title 32; subtitle 36; status 36; detail 100 | > Suggested visible-character budgets, not guaranteed fit or API hard limits: proportional glyphs, device width and accessibility text size change fit. 0 means hidden. Expanded and Lock Screen subtitle replaces status on line 2; compact and small banners retain status. Omitted subtitle uses status. Overflow shares the two-line detail area with explicit detail. Compact percentage takes precedence over status when progress is supplied; terminal shows percentage or its prompt icon and never status. - `standard` — Default progress card with an icon, two text lines, percentage, and horizontal track. - `ring` — Circular progress display with the percentage inside the gauge. - `hero` — Large status text above a bottom-edge progress track. - `terminal` — Command-prompt presentation with monospaced status and secondary detail. - `steps` — Five stage indicators for workflows better described by phases. - `approval` — Default two-choice prompt with balanced decision buttons. - `shell` — Compact command-style question and decision controls. - `verdict` — Centered question with a divider and prominent primary action. - `signal` — Decision card with distinct positive and negative controls. ## CLI reference Create persistent service endpoints and integrate the terminal client into scripts. Connection setup lives in [Get connected](/docs/quickstart#cli-install); send, ask, and activity examples live with their task guides. ### Create a persistent webhook Create a service when another process should use a persistent webhook instead of a CLI connection. `notify services create` stores the default sender title, image, and destination and returns `webhookUrl` in JSON. Later webhook sends only need their message body. ```bash notify services create \ --title "Warehouse desk" \ --image https://warehouse.example.com/icon.png \ --url https://warehouse.example.com/pickups ``` ```json { "service": { "id": "svc_...", "title": "Warehouse desk", "imageUrl": "https://warehouse.example.com/icon.png" }, "webhookUrl": "https://notify.belweave.ai/hooks/whk_your_token" } ``` Creation requires `services:write`. `--stdin` accepts the JSON fields directly. `services list` returns service metadata with credentials omitted. Reauthorize an older or narrower connection if it lacks the write scope. > Capture the returned URL securely. It authorizes sends through that service; it should not appear in public command logs. ### Output and terminal outcomes Send commands write their result as JSON on stdout and diagnostics on stderr. Most other commands also produce JSON; `apps` commands use readable lines unless `--json` is supplied. Branch on a terminal wait result, or inspect explicit status fields when reading existing prompts. The exit codes below describe outcome-bearing commands. - `0`: successful operation, or a terminal approved/yes/replied answer. - `4`: wait timeout, canceled question, or expired question. - `5`: terminal denied/no answer. - `7`: no provider accepted a push request and no device delivery was queued. - `1`: API failure; `2`: command usage error; `3`: authentication/scope failure; `6`: network failure. ```bash if notify send ask "Start the scheduled maintenance?" --approval --wait --timeout 10m; then ./run-maintenance.sh && notify send "Maintenance complete" --title "Maintenance desk" else echo "Maintenance was not approved" >&2 fi ``` ## MCP reference OAuth discovery, granted scopes, and the tool families available to a remote client. For configuration snippets, use [Connect an MCP client](/docs/quickstart#mcp-connect). ### OAuth and discovery 1. The first unauthenticated call returns `401` with a `resource_metadata` discovery pointer. The client discovers OAuth endpoints and dynamically registers. 2. Open the consent page in the browser and sign in with Apple or Google. 3. Review each permission, deselect unwanted scopes, and approve. Consent is remembered for that client and permission set. 4. The client receives a one-hour access token restricted to `/mcp`. Stay connected adds a refresh token. Tool permissions use the agent scope names. `offline_access` enables Stay connected. A client omitting scopes requests all scopes except `tokens:manage`. The authorization flow requires S256 PKCE and MCP-specific resource binding. Disconnect a client in the dashboard to revoke its access and refresh tokens. A connection with `tokens:manage` can also list OAuth connections as `kind: "oauth"` through `/api/agent/tokens` and revoke them. | Route | Purpose | | --- | --- | | `GET /.well-known/oauth-protected-resource/mcp` | Protected resource metadata (RFC 9728) for the MCP server. | | `GET /.well-known/oauth-authorization-server` | Authorization server metadata (RFC 8414): authorize, token, and registration endpoints. | ### Tool inventory MCP exposes the supported agent operations through the same handlers, validation, scopes, and limits, plus helpers such as `ask`. A missing permission becomes a tool error identifying the required scope. - `notify` creates an update; `notification_withdraw` requests its removal. - `ask` creates a choice/reply question. Notification presentations wait for at most 25 seconds per MCP call, then return the existing interaction with `timedOut: true` if it is still pending. Continue with `interactions_wait` and that same `interaction.id`; each wait call also waits at most 25 seconds. Older requested `timeoutSeconds` values up to 600 remain accepted but are capped to this waiting budget. Creation and progress reporting consume the ask budget; stalled creation or network transport can still exceed 25 seconds of wall-clock time. The prompt expiry is separate and a wait timeout never cancels it. With `presentation: live_activity`, it returns the pending interaction immediately with `accepted: 0` and `queued: N`; use `interactions_wait` or `interactions_get` with the same creating token to observe a server-confirmed answer. Queued delivery is not provider acceptance or approval. For separate steps, use `interactions_create`, `interactions_wait`, `interactions_get`, `interactions_list`, and `interactions_cancel`. - `activities_start`, `activities_update`, `activities_end`, `activities_get`, and `activities_list` manage ongoing work. - `services_*`, `devices_*`, `events_list`, `activity_feed`, `inbox_*`, `apps_*`, `billing_get`, and `tokens_*` cover account resources. - `teams_*`, `groups_*`, `oncall_*`, `pages_*`, and `connectors_*` cover shared workplace resources and internal-service connections. `webhooks_*` manages account reply endpoints. - `auth_status` inspects the current grant; `auth_revoke` disconnects it. > MCP does not add human-answer, page-acknowledgement/escalation, invite-acceptance, app-consent/pass, or token-creation operations. Sensitive webhook URLs and join links returned by creation tools are marked as secrets. ## Agent API reference Route inventory for scoped HTTP clients, with the canonical generated schemas. The CLI and MCP tools use this API; human consent is described in [Access and consent](/docs/access). ### HTTP route inventory | Route | Purpose | | --- | --- | | `GET /api/agent/auth/status` | Describe the calling token. Any scope. | | `POST /api/agent/auth/revoke` | Revoke the calling token. Any scope. | | `GET /api/agent/tokens` | List the account's API tokens (never secrets). `tokens:manage`. | | `DELETE /api/agent/tokens/:id` | Revoke another token of the same account. `tokens:manage`. | | `GET /api/agent/devices` | List registered devices. `devices:read`. | | `DELETE /api/agent/devices/:id` | Remove a device; it registers again when Notify next opens on it. `devices:write`. | | `GET /api/agent/services` | List webhook services (without webhook URLs). `services:read`. | | `POST /api/agent/services` | Create a webhook service; the webhook URL is returned once. `services:write`. | | `GET /api/agent/services/:id` | Get a webhook service. `services:read`. | | `PATCH /api/agent/services/:id` | Update a webhook service's title, avatar, or tap URL. `services:write`. | | `DELETE /api/agent/services/:id` | Delete a webhook service. `services:write`. | | `POST /api/agent/services/:id/share` | Share a webhook service with your family, or unshare it. `services:write`. | | `POST /api/agent/services/:id/rotate` | Rotate the webhook token; the new URL is returned once. `services:write`. | | `GET /api/agent/webhooks` | List saved reply webhooks with masked header values. `services:read`. | | `POST /api/agent/webhooks` | Create a reply webhook; the signing secret is returned once. `services:write`. | | `GET /api/agent/webhooks/:id` | Read a reply webhook and its last 50 deliveries. `services:read`. | | `PATCH /api/agent/webhooks/:id` | Update a reply webhook; omit headers to preserve their saved values. `services:write`. | | `DELETE /api/agent/webhooks/:id` | Delete a reply webhook. `services:write`. | | `POST /api/agent/webhooks/:id/test` | Queue a signed sample interaction.answered event (202); inspect logs for completion. `services:write`. | | `POST /api/agent/webhooks/:id/rotate-secret` | Rotate the signing secret; its replacement is returned once. `services:write`. | | `GET /api/agent/events` | Recent webhook deliveries. `events:read`. | | `GET /api/agent/activity-feed` | Merged account activity history, newest first (20 per page). `events:read`. | | `POST /api/agent/notifications` | Send a one-shot push. `notifications:send`. | | `POST /api/agent/notifications/:id/withdraw` | Withdraw an agent notification from the account's devices. `notifications:send`. | | `GET /api/agent/interactions` | List the account's pending prompts from every source. `interactions:read`. | | `POST /api/agent/interactions` | Ask a question (approval, yes/no, or reply). `interactions:create`, `notifications:send`. | | `GET /api/agent/interactions/:id` | Get a prompt created by this token. `interactions:read`. | | `GET /api/agent/interactions/:id/wait` | Long-poll a prompt created by this token until it is answered. `interactions:read`. | | `POST /api/agent/interactions/:id/cancel` | Cancel a pending prompt created by this token. `interactions:create`. | | `GET /api/agent/activities` | List Live Activities started by this token. `activities:read`. | | `POST /api/agent/activities` | Start a Live Activity. `activities:write`. | | `GET /api/agent/activities/:identifier` | Get a Live Activity by ID or key. `activities:read`. | | `PATCH /api/agent/activities/:identifier` | Update a Live Activity. `activities:write`. | | `POST /api/agent/activities/:identifier/end` | End a Live Activity. `activities:write`. | | `GET /api/agent/apps` | List web apps. `apps:read`. | | `POST /api/agent/apps` | Register a web app (an existing URL is updated). `apps:write`. | | `GET /api/agent/apps/:id` | Get a web app. `apps:read`. | | `PATCH /api/agent/apps/:id` | Update a web app's name, URL, icon, or project. `apps:write`. | | `DELETE /api/agent/apps/:id` | Remove a web app. `apps:write`. | | `POST /api/agent/apps/:id/revoke` | Revoke sign-in consent; the owner approves again on the next sign-in request. `apps:write`. | | `POST /api/agent/apps/:id/share` | Move an app into a team, or (teamId null) back to your own apps. `apps:write`. | | `GET /api/agent/teams` | List the teams you belong to. `teams:read`. | | `POST /api/agent/teams` | Create a family with `kind: family`; you become its owner. Omitting `kind` or using `kind: team` returns `403` because Teams is TBD. `teams:write`. | | `GET /api/agent/teams/:id` | A team and its members. `teams:read`. | | `PATCH /api/agent/teams/:id` | Update a team (name: admin; auto-join and retention: owner). `teams:write`. | | `DELETE /api/agent/teams/:id` | Delete a team (owner); its apps return to the members who added them. `teams:write`. | | `POST /api/agent/teams/:id/leave` | Leave a team (the owner must transfer ownership first). `teams:write`. | | `PATCH /api/agent/teams/:id/members/:userId` | Change a member's role; `owner` transfers ownership (owner only). `teams:write`. | | `DELETE /api/agent/teams/:id/members/:userId` | Remove a member (admin or owner); the owner cannot be removed. `teams:write`. | | `GET /api/agent/teams/:id/invites` | List invites (admin or owner). `teams:read`. | | `POST /api/agent/teams/:id/invites` | Create a 7-day join link (admin or owner). `teams:write`. | | `DELETE /api/agent/teams/:id/invites/:inviteId` | Revoke an invite. `teams:write`. | | `GET /api/agent/teams/:id/apps` | The team's shared apps. `teams:read`, `apps:read`. | | `GET /api/agent/teams/:id/groups` | The team's device groups. `teams:read`. | | `POST /api/agent/teams/:id/groups` | Create a device group (admin or owner). `teams:write`. | | `GET /api/agent/teams/:id/groups/:groupId` | A device group. `teams:read`. | | `PATCH /api/agent/teams/:id/groups/:groupId` | Update a device group (admin or owner). `teams:write`. | | `DELETE /api/agent/teams/:id/groups/:groupId` | Delete a device group (admin or owner). `teams:write`. | | `GET /api/agent/teams/:id/devices` | Devices belonging to team members (admin or owner). `teams:read`. | | `POST /api/agent/teams/:id/domain` | Request a DNS TXT challenge for an email domain (owner). `teams:write`. | | `DELETE /api/agent/teams/:id/domain` | Clear the pending or verified domain (owner). `teams:write`. | | `POST /api/agent/teams/:id/domain/verify` | Verify the pending domain's DNS TXT record (owner). `teams:write`. | | `GET /api/agent/teams/:id/audit` | Audit log (admin or owner); members receive 403. `teams:read`. | | `GET /api/agent/teams/:id/oncall` | The team's on-call groups with current and upcoming shifts. `oncall:read`. | | `POST /api/agent/teams/:id/oncall` | Create an on-call group (admin or owner). `oncall:write`. | | `GET /api/agent/teams/:id/pages` | The team's pages, newest first. `oncall:read`. | | `GET /api/agent/oncall/me` | Your upcoming shifts and the open pages that notified you. `oncall:read`. | | `GET /api/agent/oncall/:groupId` | An on-call group. `oncall:read`. | | `PATCH /api/agent/oncall/:groupId` | Edit a group's name, rotation, or escalation (admin or owner). `oncall:write`. | | `DELETE /api/agent/oncall/:groupId` | Delete an on-call group (admin or owner). `oncall:write`. | | `POST /api/agent/oncall/:groupId/overrides` | Put someone on call for a window (admins: anyone; members: themselves). `oncall:write`. | | `DELETE /api/agent/oncall/:groupId/overrides/:overrideId` | Remove an override. `oncall:write`. | | `POST /api/agent/oncall/:groupId/pages` | Page the group: whoever is on call first, then escalation. `oncall:write`. | | `GET /api/agent/pages/:id` | A page. `oncall:read`. | | `GET /api/agent/connectors` | List tunnel connectors. `connectors:read`. | | `POST /api/agent/connectors` | Register a tunnel connector; the callback HMAC secret is returned once. `connectors:write`. | | `GET /api/agent/connectors/:id` | A tunnel connector. `connectors:read`. | | `PATCH /api/agent/connectors/:id` | Update a connector's name, routes, or on-call group. `connectors:write`. | | `DELETE /api/agent/connectors/:id` | Remove a tunnel connector. `connectors:write`. | | `POST /api/agent/connectors/:id/heartbeat` | Mark a connector online and refresh its last-seen time. `connectors:write`. | | `POST /api/agent/connectors/:id/access` | Gateway-only pass exchange or access revalidation; requires the connector's credential and a phone-issued pass. `connectors:write`. | | `POST /api/agent/connectors/:id/callback-access` | Gateway-only authorization of a signed callback against its saved target and durable replay receipt. `connectors:write`. | | `POST /api/agent/connectors/:id/rotate-secret` | Rotate a connector callback HMAC secret; the new secret is returned once. `connectors:write`. | | `POST /api/agent/pages/:id/resolve` | Resolve a page; anyone still being paged is cleared. `oncall:write`. | | `GET /api/agent/inbox/projects` | Inbox projects with unread counts. `inbox:read`. | | `GET /api/agent/inbox/notifications` | Inbox notifications, newest first, with cursor pagination. `inbox:read`. | | `GET /api/agent/inbox/notifications/:id` | One inbox notification with its full body. `inbox:read`. | | `POST /api/agent/inbox/notifications/read-all` | Mark everything up to a list response's readThroughToken read. `inbox:write`. | | `POST /api/agent/inbox/notifications/:id/read` | Mark one notification read. `inbox:write`. | | `POST /api/agent/inbox/notifications/:id/unread` | Mark one notification unread. `inbox:write`. | | `GET /api/agent/billing` | Plan, limits, and remaining usage. `billing:read`. | ```bash notify inbox list --unread --limit 20 notify inbox read-all --project unfiled notify interaction list notify send withdraw anot_... notify services rotate svc_... notify apps update app_example_warehouse --name "Maintenance board" notify billing notify tokens list # needs --scope tokens:manage at login ``` ### Direct interaction fields Direct HTTP and MCP questions use the interaction schema rather than the webhook’s nested `response` object. Create requires `interactions:create` and `notifications:send`; reading and waiting require `interactions:read`. A successful create only establishes the pending request. | Field | Type | Description | | --- | --- | --- | | `title` | string, required | Sender title, 1–80 characters after trimming. | | `prompt` | string, required | Question, 1–2,000 characters after trimming. live_activity presentation caps this at 240. | | `kind` | enum, required | `approval`, `yes_no`, or `reply`. Note that webhook type `text` maps to agent kind `reply`. | | `expiresInSeconds` | integer | 30–86,400 seconds, default 900. live_activity presentation cannot exceed 28,800. | | `presentation` | enum | `notification` (default) or `live_activity`. The latter uses ActivityKit on capable iOS and an ordinary actionable alert on native Android. | | `style` | enum | Only with live_activity: `approval`, `shell`, `verdict`, or `signal`. | | `primaryLabel / secondaryLabel` | string | Only with live_activity: 1–24 characters each, single-line labels. Canonical returned actions do not change. | | `imageUrl / url` | string | Optional sender image and tap destination, using notification validation. Not accepted with live_activity. | | `deviceIds / group` | string[] / string | Optional selected devices or authorized workplace group. Supply one targeting form, not both. | | `callback` | object | Classic URL/token or connector-target answered callback; see [per-question callbacks](/docs/notification-api#response-callbacks). | | `webhookId` | string | Optional saved account reply endpoint (`whk_…`) in addition to applicable defaults. | ```bash curl -X POST https://notify.belweave.ai/api/agent/interactions -H "Authorization: Bearer $NOTIFY_TOKEN" -H 'Content-Type: application/json' -d '{"title":"Warehouse desk","prompt":"Hold package PKG-42 until tomorrow?","kind":"approval","expiresInSeconds":900}' ``` > For live_activity, use approval or yes_no, never reply. The generated [OpenAPI schemas](/docs/agent-api#agent-api-openapi) describe create, wait, get, list, and cancel HTTP requests. Only a valid human answer can settle the question as approved or denied. ### Generated OpenAPI schemas Use the OpenAPI 3.1 document for exact HTTP schemas and automation. It includes `x-notify-scopes` and request validators generated from the server’s schemas. The route inventory above is kept in step with that implementation. OpenAPI document: ```text https://notify.belweave.ai/api/agent/openapi.json ``` ## Reach an internal service Expose a private HTTP service through Notify Connector and the OpenTunnel relay. The public hostname reaches the service; a verified Notify pass or signed callback still determines who may use it. ### Relay and connector lifecycle The relay lives at `https://api.clawchest.com`; tunnel addresses are `https://.clawchest.com` and named routes `https://..clawchest.com`. Tunnel creation uses a Notify agent token (`ntf_…`), introspected at `/api/relay/introspect`. A public tunnel address provides reachability, not visitor authorization. - `@belweave/notify-connector` uses OpenTunnel 0.5.1, declares public routes, registers web apps, and sends heartbeats. Notify retains its Cloudflare relay and AWS backend. - After five minutes without a heartbeat, the connector is marked offline. Its configured `oncallGroupId` can receive a page. - A callback `target` containing `connectorId`, `route`, and `path` follows the connector’s live address at delivery time. ### Expose a local port Run the web app and the Notify CLI on your computer. Keep both processes running, then open the registered app from Apps in Notify on your phone. This example connects a local warehouse dashboard on port 3000: ```bash notify auth login notify connect 3000 --name warehouse # https://warehouse..clawchest.com ``` OpenTunnel 0.5.1 adds optional PROXY protocol v1/v2 support. Use `notify connect 3000 --name warehouse --proxy-protocol v2` only when that upstream listener explicitly accepts PROXY protocol before HTTP, or before TLS for an HTTPS upstream. Ordinary web servers should omit the flag. In the SDK, a route can be `{ target: "127.0.0.1:3000", proxyProtocol: "v2" }`; plain string routes keep their existing behavior. The authenticated gateway remains mandatory and forwards the preamble only after authorization. Visitor addresses come from the relay connection, not caller-supplied HTTP forwarding headers. The connector adopts the 0.5.1 client’s reconnect and transient-failure handling. Certificate and tunnel state remain in memory for the running connector; restarting does not restore a persisted tunnel identity or certificate. The named origin is registered as a Notify web app. The connector's mandatory gateway validates a one-time Notify pass, then rechecks current access for every page, asset, API request and WebSocket. The exact origin prevents a sibling route from reusing its pass. The updated `notify connect` and `connectors rotate-secret` save the callback signing secret in a mode-0600 file and print only its path, including with `NOTIFY_TOKEN`; the environment token itself is not saved. The normal locations are `~/Library/Application Support/notify/connectors` on macOS and `~/.config/notify/connectors` on Linux, or beside `NOTIFY_CONFIG` when set. Older CLI releases printed the secret to stderr; update before running in an agent log. Default logins include connector read/write scopes; narrowed or older logins need `connectors:write`. Rotate with `notify connectors rotate-secret `. ### Return an answer to a private receiver To return an approval to a private receiver, register its listening port as a connector route and save that route plus the receiver path on the interaction. Notify signs the answer and sends it through the fixed `/.notify/callback` gateway ingress. The gateway authorizes the saved target, current workspace access and durable replay receipt before forwarding; callbacks cannot bypass sign-in on ordinary app routes. ```bash # On the laptop: a tiny receiver on port 8780, then # notify connect 8780 --name cb # From CI or an agent: notify send ask "Approve the maintenance window?" --approval --stdin <<'JSON' { "callback": { "target": { "connectorId": "conn_example_warehouse", "route": "cb", "path": "/approve" } } } JSON ``` Verify `Notify-Signature: v1=` as HMAC-SHA256 of `{timestamp}.{rawBody}`, using `Notify-Timestamp` and the connector’s `callbackSecret`. A classic URL/token callback instead uses the callback token as its HMAC key and also sends a bearer Authorization header. Accept timestamps only within five minutes and reject future values. ```json { "type": "notification.response", "interactionId": "int_example_pickup", "actionDigest": "", "eventId": "evt_...", "kind": "approval", "status": "approved", "action": "approve", "text": null, "respondedAt": "2026-10-09T12:00:00.000Z" } ``` > The SDK exports `verifyCallbackSignature` for this format. Verify before acting and deduplicate by `interactionId` because retries can repeat answers. Legacy service receivers may also use `eventId`; agent-created asks can have a null event ID. Map a verified canonical action to a known operation; do not execute text from the callback as a shell command. ### SDK middleware for a private site The connector registers a hosted-app origin and places its mandatory gateway in front of the private dashboard. The gateway validates ES256, exact issuer and origin audience, expiry, app/workspace claims, durable one-time replay consumption and current access. It covers static assets and WebSocket upgrades as well as application requests. Your server still authorizes access to its own data using the gateway’s verified identity, and its listener must not be publicly reachable around that gateway. For direct integrations, the SDK also supplies `honoNotifyPass`, `expressNotifyPass`, and `fastifyNotifyPass`. Configure issuer `https://notify.belweave.ai`, the exact audience, and the required atomic durable `consumeJti` store; the connector README includes a database example. Missing or unavailable replay storage denies access. These raw-pass helpers are separate from the gateway’s short-lived sessions and continuous revocation checks. `verifyNotifyPass` alone verifies cryptography, not replay or current membership. ## Existing team administration These operations support existing workplace teams. New team signup is unavailable. Teams share organization resources while members keep their own accounts and sign-in choices; [families](/docs/family) have a smaller, separate feature set. ### Availability and usage New workplace team creation is unavailable and returns `403`. Personal and Family plans are available for new accounts. Teams pricing is to be determined; existing teams keep their current capabilities. Teams is TBD and new workplace signup is unavailable. Existing teams retain free access without seat charges; this administration reference serves those teams. > Pages have no monthly notification cap, whether raised by an agent connection or service. Administrative notices about invitations and shared apps are excluded from usage accounting. ### Role permissions Existing workplace teams assign each account one of these roles. These permissions apply to teams, not Family groups: | Field | Type | Description | | --- | --- | --- | | `owner` | one per team | Everything an admin can do, plus deleting the team, managing billing, transferring ownership, verifying the email domain, auto-join, and retention. The owner must transfer ownership before leaving. | | `admin` | role | Renames the team, invites and removes members, changes roles, removes any team app, creates and edits on-call and device groups, and reads the audit log. | | `member` | role | Uses and adds team apps, raises and acknowledges pages, and schedules overrides for themselves. | Assigning `owner` transfers ownership and changes the former owner to `admin`. Removing a member immediately prevents new team-app passes and removes that person from rotations. ### Issue a workplace invitation An owner or admin can issue a revocable, single-person invite that lasts seven days. Supplying `email` also alerts an existing Notify account at that address. A `domain` restriction must use the organization’s verified domain. ```bash notify teams invite team_... --email teammate@example.com --role member notify teams invite team_... --domain acme.com --role member # { "invite": { ... }, "code": "…", "url": "https://notify.belweave.ai/join/…" } ``` The join page identifies the team, inviter, and assigned role. The recipient signs in to accept; an agent may prepare an invitation but cannot join on a person’s behalf. ### Shared workplace apps Members can add a [hosted app](/docs/web-apps) or share one they originally added with `notify apps share app_... --team team_...`. Use `--personal` to return it to personal use. Other members receive a notice and see the shared app. - Each viewer chooses sign-in consent and profile sharing independently. - A team-app pass carries pairwise `sub`, `team_id`, and the current `team_role`, checked when issued. The app server still decides authorization. - The original contributor and team admins can rename/remove the app. Deleting the team returns shared apps to their original contributors. ### Target a device group A device group (`dgrp_…`) selects workplace members and/or individual devices; it is not an on-call rotation. Owners/admins manage groups. Targeting is allowed for those admins/owners and current group members; other teammates get `403`, and cross-team IDs return `404`. `group` is exclusive with `deviceIds` and `oncall`. Group questions settle on the first valid answer from a current group member. ```bash notify groups create --team team_... --name Maintenance --members user_a,user_b notify send "Cold-room sensor is offline" --group dgrp_... notify send ask "Start maintenance?" --approval --group dgrp_... notify activity start --title Maintenance --status Running --group dgrp_... notify page ocg_example_maintenance "Pager" --group dgrp_... ``` ### Domains and retention Organization settings belong to the existing team. To claim a domain, the owner must have a verified email there. Requesting a challenge stores a pending token; publish `_notify-verification.` with TXT value `notify-verify=` and run verify. Only successful verification establishes the unique `verifiedDomain`. Pending domains need not be unique. Public email providers and Apple relay domains cannot be claimed. Auto-join is owner-controlled, off by default, and requires a verified domain; an eligible user must have `emailVerified: true` and an available seat. SSO/SCIM are future work. Retention may be `7`, `30`, `90`, or `365` days, or `null` indefinitely. It applies to organization-targeted notifications, prompts, and pages, not personal sends. Hourly cleanup removes expired rows in bounded indexed batches and resumes shortly after a time-budget stop. It preserves pending prompts, their events, and active activities. ```bash notify teams domain team_... acme.com # Add TXT _notify-verification.acme.com = notify-verify= notify teams verify-domain team_... notify teams update team_... --auto-join --retention 30 notify teams update team_... --no-domain --no-retention ``` ### Review organization events Owners and admins can inspect the append-only team audit in the dashboard or `GET /api/agent/teams/:id/audit`; members receive `403`. Filters include an inclusive `from`. CSV export walks at most 50,000 rows and reports additional rows with `X-Audit-Truncated: true`. Records contain actor, target, IP, user agent, and timestamp without message bodies or replies. Covered events include invites, membership/roles, deletion, device groups, app sharing, org-targeted notifications/activities/pages, answered approvals, and settings. The human responder is the actor for an answer. Token creation/revocation is excluded from team audit. ## Page a responder For existing workplace teams, choose the current responder and escalate unanswered pages. This chapter covers rotations, page creation, and the human acknowledgement that stops escalation. ### Schedule responders An existing workplace team’s owner or admin can define a responder order. Daily/weekly handoffs use an IANA time zone and local clock, preserving the chosen local time through daylight-saving changes. ```bash notify oncall create --team team_... --name Primary \ --members user_a,user_b,user_c --period weekly \ --handoff 09:00 --timezone America/New_York ``` If `startsAt` is absent, the first responder’s shift begins at the most recent handoff. A timed override replaces the rotation during its window; members can schedule their own coverage. Group reads include current and upcoming shifts. ### Escalation and deduplication A new page contacts the current responder, or the entire group if no responder is scheduled. Configured escalation continues until an acknowledgement stops it: | Field | Type | Description | | --- | --- | --- | | `afterMinutes` | integer | Minutes after the previous step (or the page) without an acknowledgement. | | `target` | next \| group | `next` pages the next person in the rotation; `group` pages everyone in it who has not been paged yet. | The default schedule contacts the next responder after five minutes, then the group ten minutes later. Repeated `dedupKey` values join an existing open page and increment `repeatCount` without issuing another initial page. ```bash notify page ocg_example_maintenance "Cold-room temperature above threshold" \ --body "Sensor reports 12°C since 14:02" --dedup-key cold-room-temperature ``` ### Page from a service A service can route a message to a rotation using `oncall`. Its owner must belong to that workplace team. Do not combine it with `deviceIds`, `group`, or `response`. The webhook `Idempotency-Key` becomes the page deduplication key. ```json { "title": "Cold-room monitor", "body": "Temperature above threshold for 5 minutes", "oncall": "ocg_example_maintenance" } ``` The page response includes `pageId` and provider-acceptance count `delivered`. Keep the body within 2,000 characters. CLI equivalents are `notify page` and `notify send --oncall`. ### Acknowledge, escalate, and resolve A current teammate can acknowledge through a supported Lock Screen action, the app, or the website. The first accepted acknowledgement stops escalation; later acknowledgements are refused. Notify requests clearing the other devices’ alerts. Escalate immediately advances to the next step. > Acknowledgement and escalation require a person. Agents can raise, inspect, and resolve pages, but cannot acknowledge or escalate as if a person had taken responsibility. When the incident is finished, resolve it on the website or with `notify pages resolve page_... --note "Maintenance completed"`. Resolution also stops escalation on a page that was never acknowledged. # Notify Agent API # Describe the calling token GET /api/agent/auth/status. Required scopes: . HTML: https://notify.belweave.ai/docs/api/getAuthStatus Required scopes: ```http GET /api/agent/auth/status Authorization: Bearer ntf_your_token ``` ## Operation schema ```json { "summary": "Describe the calling token", "operationId": "getAuthStatus", "security": [ { "bearer": [] } ], "x-notify-scopes": [], "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/TokenStatus" } } } }, "400": { "description": "Invalid request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "401": { "description": "Missing, unknown, revoked, or expired token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "The token lacks a required scope or the account lacks permission", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "Not found for this account", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } ``` Resolve schema references against [OpenAPI](https://notify.belweave.ai/openapi.json). # Revoke the calling token POST /api/agent/auth/revoke. Required scopes: . HTML: https://notify.belweave.ai/docs/api/postAuthRevoke Required scopes: ```http POST /api/agent/auth/revoke Authorization: Bearer ntf_your_token ``` ## Operation schema ```json { "summary": "Revoke the calling token", "operationId": "postAuthRevoke", "security": [ { "bearer": [] } ], "x-notify-scopes": [], "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "type": "object", "properties": { "ok": { "const": true } }, "required": [ "ok" ] } } } }, "400": { "description": "Invalid request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "401": { "description": "Missing, unknown, revoked, or expired token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "The token lacks a required scope or the account lacks permission", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "Not found for this account", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } ``` Resolve schema references against [OpenAPI](https://notify.belweave.ai/openapi.json). # List the account's API tokens (never secrets) Connected MCP/OAuth clients appear as `kind: "oauth"`; revoking one signs that client out (access and refresh tokens). There is no create route: an agent token cannot mint tokens, so it can never escalate its own scopes or outlive its revocation. Create tokens from a signed-in session or `notify auth login`. HTML: https://notify.belweave.ai/docs/api/getTokens Required scopes: `tokens:manage` ```http GET /api/agent/tokens Authorization: Bearer ntf_your_token ``` ## Operation schema ```json { "summary": "List the account's API tokens (never secrets)", "description": "Connected MCP/OAuth clients appear as `kind: \"oauth\"`; revoking one signs that client out (access and refresh tokens). There is no create route: an agent token cannot mint tokens, so it can never escalate its own scopes or outlive its revocation. Create tokens from a signed-in session or `notify auth login`.", "operationId": "getTokens", "security": [ { "bearer": [ "tokens:manage" ] } ], "x-notify-scopes": [ "tokens:manage" ], "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "type": "object", "properties": { "tokens": { "type": "array", "items": { "$ref": "#/components/schemas/ApiToken" } } }, "required": [ "tokens" ] } } } }, "400": { "description": "Invalid request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "401": { "description": "Missing, unknown, revoked, or expired token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "The token lacks a required scope or the account lacks permission", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "Not found for this account", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } ``` Resolve schema references against [OpenAPI](https://notify.belweave.ai/openapi.json). # Revoke another token of the same account DELETE /api/agent/tokens/{id}. Required scopes: tokens:manage. HTML: https://notify.belweave.ai/docs/api/deleteTokensId Required scopes: `tokens:manage` ```http DELETE /api/agent/tokens/{id} Authorization: Bearer ntf_your_token ``` ## Operation schema ```json { "summary": "Revoke another token of the same account", "operationId": "deleteTokensId", "security": [ { "bearer": [ "tokens:manage" ] } ], "x-notify-scopes": [ "tokens:manage" ], "parameters": [ { "name": "id", "in": "path", "schema": { "type": "string" }, "required": true } ], "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "type": "object", "properties": { "ok": { "const": true } }, "required": [ "ok" ] } } } }, "400": { "description": "Invalid request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "401": { "description": "Missing, unknown, revoked, or expired token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "The token lacks a required scope or the account lacks permission", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "Not found for this account", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } ``` Resolve schema references against [OpenAPI](https://notify.belweave.ai/openapi.json). # List registered devices Registration is phone-only: only the Notify app holds a push token. HTML: https://notify.belweave.ai/docs/api/getDevices Required scopes: `devices:read` ```http GET /api/agent/devices Authorization: Bearer ntf_your_token ``` ## Operation schema ```json { "summary": "List registered devices", "description": "Registration is phone-only: only the Notify app holds a push token.", "operationId": "getDevices", "security": [ { "bearer": [ "devices:read" ] } ], "x-notify-scopes": [ "devices:read" ], "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "type": "object", "properties": { "devices": { "type": "array", "items": { "$ref": "#/components/schemas/Device" } } }, "required": [ "devices" ] } } } }, "400": { "description": "Invalid request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "401": { "description": "Missing, unknown, revoked, or expired token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "The token lacks a required scope or the account lacks permission", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "Not found for this account", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } ``` Resolve schema references against [OpenAPI](https://notify.belweave.ai/openapi.json). # Remove a device; it registers again when Notify next opens on it DELETE /api/agent/devices/{id}. Required scopes: devices:write. HTML: https://notify.belweave.ai/docs/api/deleteDevicesId Required scopes: `devices:write` ```http DELETE /api/agent/devices/{id} Authorization: Bearer ntf_your_token ``` ## Operation schema ```json { "summary": "Remove a device; it registers again when Notify next opens on it", "operationId": "deleteDevicesId", "security": [ { "bearer": [ "devices:write" ] } ], "x-notify-scopes": [ "devices:write" ], "parameters": [ { "name": "id", "in": "path", "schema": { "type": "string" }, "required": true } ], "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "type": "object", "properties": { "ok": { "const": true } }, "required": [ "ok" ] } } } }, "400": { "description": "Invalid request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "401": { "description": "Missing, unknown, revoked, or expired token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "The token lacks a required scope or the account lacks permission", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "Not found for this account", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } ``` Resolve schema references against [OpenAPI](https://notify.belweave.ai/openapi.json). # List webhook services (without webhook URLs) GET /api/agent/services. Required scopes: services:read. HTML: https://notify.belweave.ai/docs/api/getServices Required scopes: `services:read` ```http GET /api/agent/services Authorization: Bearer ntf_your_token ``` ## Operation schema ```json { "summary": "List webhook services (without webhook URLs)", "operationId": "getServices", "security": [ { "bearer": [ "services:read" ] } ], "x-notify-scopes": [ "services:read" ], "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "type": "object", "properties": { "services": { "type": "array", "items": { "$ref": "#/components/schemas/AgentService" } } }, "required": [ "services" ] } } } }, "400": { "description": "Invalid request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "401": { "description": "Missing, unknown, revoked, or expired token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "The token lacks a required scope or the account lacks permission", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "Not found for this account", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } ``` Resolve schema references against [OpenAPI](https://notify.belweave.ai/openapi.json). # Create a webhook service; the webhook URL is returned once POST /api/agent/services. Required scopes: services:write. HTML: https://notify.belweave.ai/docs/api/postServices Required scopes: `services:write` ```http POST /api/agent/services Authorization: Bearer ntf_your_token ``` ## Operation schema ```json { "summary": "Create a webhook service; the webhook URL is returned once", "operationId": "postServices", "security": [ { "bearer": [ "services:write" ] } ], "x-notify-scopes": [ "services:write" ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "defaultReplyWebhookId": { "anyOf": [ { "type": "string", "pattern": "^whk_[A-Za-z0-9_-]{1,100}$" }, { "type": "null" } ] }, "title": { "type": "string", "minLength": 1, "maxLength": 80 }, "imageUrl": { "anyOf": [ { "type": "string", "maxLength": 2048, "format": "uri" }, { "type": "null" } ] }, "url": { "anyOf": [ { "type": "string", "maxLength": 2048, "format": "uri" }, { "type": "null" } ] } }, "required": [ "title" ] } } } }, "responses": { "201": { "description": "Success", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ServiceCreated" } } } }, "400": { "description": "Invalid request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "401": { "description": "Missing, unknown, revoked, or expired token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "The token lacks a required scope or the account lacks permission", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "Not found for this account", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } ``` Resolve schema references against [OpenAPI](https://notify.belweave.ai/openapi.json). # Get a webhook service GET /api/agent/services/{id}. Required scopes: services:read. HTML: https://notify.belweave.ai/docs/api/getServicesId Required scopes: `services:read` ```http GET /api/agent/services/{id} Authorization: Bearer ntf_your_token ``` ## Operation schema ```json { "summary": "Get a webhook service", "operationId": "getServicesId", "security": [ { "bearer": [ "services:read" ] } ], "x-notify-scopes": [ "services:read" ], "parameters": [ { "name": "id", "in": "path", "schema": { "type": "string" }, "required": true } ], "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "type": "object", "properties": { "service": { "$ref": "#/components/schemas/AgentService" } }, "required": [ "service" ] } } } }, "400": { "description": "Invalid request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "401": { "description": "Missing, unknown, revoked, or expired token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "The token lacks a required scope or the account lacks permission", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "Not found for this account", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } ``` Resolve schema references against [OpenAPI](https://notify.belweave.ai/openapi.json). # Update a webhook service's title, avatar, or tap URL PATCH /api/agent/services/{id}. Required scopes: services:write. HTML: https://notify.belweave.ai/docs/api/patchServicesId Required scopes: `services:write` ```http PATCH /api/agent/services/{id} Authorization: Bearer ntf_your_token ``` ## Operation schema ```json { "summary": "Update a webhook service's title, avatar, or tap URL", "operationId": "patchServicesId", "security": [ { "bearer": [ "services:write" ] } ], "x-notify-scopes": [ "services:write" ], "parameters": [ { "name": "id", "in": "path", "schema": { "type": "string" }, "required": true } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "defaultReplyWebhookId": { "anyOf": [ { "type": "string", "pattern": "^whk_[A-Za-z0-9_-]{1,100}$" }, { "type": "null" } ] }, "title": { "type": "string", "minLength": 1, "maxLength": 80 }, "imageUrl": { "anyOf": [ { "type": "string", "maxLength": 2048, "format": "uri" }, { "type": "null" } ] }, "url": { "anyOf": [ { "type": "string", "maxLength": 2048, "format": "uri" }, { "type": "null" } ] } } } } } }, "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "type": "object", "properties": { "service": { "$ref": "#/components/schemas/AgentService" } }, "required": [ "service" ] } } } }, "400": { "description": "Invalid request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "401": { "description": "Missing, unknown, revoked, or expired token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "The token lacks a required scope or the account lacks permission", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "Not found for this account", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } ``` Resolve schema references against [OpenAPI](https://notify.belweave.ai/openapi.json). # Delete a webhook service DELETE /api/agent/services/{id}. Required scopes: services:write. HTML: https://notify.belweave.ai/docs/api/deleteServicesId Required scopes: `services:write` ```http DELETE /api/agent/services/{id} Authorization: Bearer ntf_your_token ``` ## Operation schema ```json { "summary": "Delete a webhook service", "operationId": "deleteServicesId", "security": [ { "bearer": [ "services:write" ] } ], "x-notify-scopes": [ "services:write" ], "parameters": [ { "name": "id", "in": "path", "schema": { "type": "string" }, "required": true } ], "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "type": "object", "properties": { "ok": { "const": true } }, "required": [ "ok" ] } } } }, "400": { "description": "Invalid request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "401": { "description": "Missing, unknown, revoked, or expired token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "The token lacks a required scope or the account lacks permission", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "Not found for this account", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } ``` Resolve schema references against [OpenAPI](https://notify.belweave.ai/openapi.json). # Share a webhook service with your family, or unshare it Only the original creator can share or unshare. Set familyId to your family ID, or null to unshare. Accepted family members receive shared alerts with independent inbox read state. Family owners can rename or delete shared services, but only the original creator can rotate or see the webhook token. A creator leaving the family automatically unshares their services. HTML: https://notify.belweave.ai/docs/api/postServicesIdShare Required scopes: `services:write` ```http POST /api/agent/services/{id}/share Authorization: Bearer ntf_your_token ``` ## Operation schema ```json { "summary": "Share a webhook service with your family, or unshare it", "description": "Only the original creator can share or unshare. Set familyId to your family ID, or null to unshare. Accepted family members receive shared alerts with independent inbox read state. Family owners can rename or delete shared services, but only the original creator can rotate or see the webhook token. A creator leaving the family automatically unshares their services.", "operationId": "postServicesIdShare", "security": [ { "bearer": [ "services:write" ] } ], "x-notify-scopes": [ "services:write" ], "parameters": [ { "name": "id", "in": "path", "schema": { "type": "string" }, "required": true } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "familyId": { "anyOf": [ { "type": "string", "minLength": 1 }, { "type": "null" } ] } }, "required": [ "familyId" ], "additionalProperties": false } } } }, "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "type": "object", "properties": { "service": { "$ref": "#/components/schemas/AgentService" } }, "required": [ "service" ] } } } }, "400": { "description": "Invalid request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "401": { "description": "Missing, unknown, revoked, or expired token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "The token lacks a required scope or the account lacks permission", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "Not found for this account", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } ``` Resolve schema references against [OpenAPI](https://notify.belweave.ai/openapi.json). # Rotate the webhook token; the new URL is returned once POST /api/agent/services/{id}/rotate. Required scopes: services:write. HTML: https://notify.belweave.ai/docs/api/postServicesIdRotate Required scopes: `services:write` ```http POST /api/agent/services/{id}/rotate Authorization: Bearer ntf_your_token ``` ## Operation schema ```json { "summary": "Rotate the webhook token; the new URL is returned once", "operationId": "postServicesIdRotate", "security": [ { "bearer": [ "services:write" ] } ], "x-notify-scopes": [ "services:write" ], "parameters": [ { "name": "id", "in": "path", "schema": { "type": "string" }, "required": true } ], "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ServiceCreated" } } } }, "400": { "description": "Invalid request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "401": { "description": "Missing, unknown, revoked, or expired token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "The token lacks a required scope or the account lacks permission", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "Not found for this account", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } ``` Resolve schema references against [OpenAPI](https://notify.belweave.ai/openapi.json). # List the account's saved Reply webhooks Signing secrets and stored header values are never returned by read operations. HTML: https://notify.belweave.ai/docs/api/getWebhooks Required scopes: `services:read` ```http GET /api/agent/webhooks Authorization: Bearer ntf_your_token ``` ## Operation schema ```json { "summary": "List the account's saved Reply webhooks", "description": "Signing secrets and stored header values are never returned by read operations.", "operationId": "getWebhooks", "security": [ { "bearer": [ "services:read" ] } ], "x-notify-scopes": [ "services:read" ], "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "type": "object", "properties": { "webhooks": { "type": "array", "items": { "$ref": "#/components/schemas/ReplyWebhook" } } }, "required": [ "webhooks" ] } } } }, "400": { "description": "Invalid request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "401": { "description": "Missing, unknown, revoked, or expired token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "The token lacks a required scope or the account lacks permission", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "Not found for this account", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } ``` Resolve schema references against [OpenAPI](https://notify.belweave.ai/openapi.json). # Create a Reply webhook; its signing secret is returned once Save an HTTPS endpoint with a generic, Grok, or Slack payload preset. Pass its whk_ ID as webhookId when creating a prompt, set a service's defaultReplyWebhookId, or set isDefault:true for account replies including MCP/API prompts without services. Account and selected endpoints each receive one event, deduplicated by endpoint ID. Store the signingSecret securely to verify signed deliveries. HTML: https://notify.belweave.ai/docs/api/postWebhooks Required scopes: `services:write` ```http POST /api/agent/webhooks Authorization: Bearer ntf_your_token ``` ## Operation schema ```json { "summary": "Create a Reply webhook; its signing secret is returned once", "description": "Save an HTTPS endpoint with a generic, Grok, or Slack payload preset. Pass its whk_ ID as webhookId when creating a prompt, set a service's defaultReplyWebhookId, or set isDefault:true for account replies including MCP/API prompts without services. Account and selected endpoints each receive one event, deduplicated by endpoint ID. Store the signingSecret securely to verify signed deliveries.", "operationId": "postWebhooks", "security": [ { "bearer": [ "services:write" ] } ], "x-notify-scopes": [ "services:write" ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "name": { "type": "string", "minLength": 1, "maxLength": 80 }, "url": { "type": "string", "maxLength": 2048, "format": "uri" }, "preset": { "default": "generic", "type": "string", "enum": [ "generic", "grok", "slack" ] }, "headers": { "default": [], "maxItems": 16, "type": "array", "items": { "type": "object", "properties": { "name": { "type": "string", "minLength": 1, "maxLength": 128, "pattern": "^[!#$%&'*+.^_`|~0-9A-Za-z-]+$" }, "value": { "type": "string", "minLength": 1, "maxLength": 4096 } }, "required": [ "name", "value" ] } }, "events": { "minItems": 1, "maxItems": 3, "type": "array", "items": { "type": "string", "enum": [ "interaction.answered", "interaction.expired", "interaction.canceled" ] } }, "enabled": { "default": true, "type": "boolean" }, "isDefault": { "default": false, "type": "boolean" } }, "required": [ "name", "url" ] } } } }, "responses": { "201": { "description": "Success", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ReplyWebhookCreated" } } } }, "400": { "description": "Invalid request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "401": { "description": "Missing, unknown, revoked, or expired token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "The token lacks a required scope or the account lacks permission", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "Not found for this account", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } ``` Resolve schema references against [OpenAPI](https://notify.belweave.ai/openapi.json). # Get a Reply webhook and its recent deliveries GET /api/agent/webhooks/{id}. Required scopes: services:read. HTML: https://notify.belweave.ai/docs/api/getWebhooksId Required scopes: `services:read` ```http GET /api/agent/webhooks/{id} Authorization: Bearer ntf_your_token ``` ## Operation schema ```json { "summary": "Get a Reply webhook and its recent deliveries", "operationId": "getWebhooksId", "security": [ { "bearer": [ "services:read" ] } ], "x-notify-scopes": [ "services:read" ], "parameters": [ { "name": "id", "in": "path", "schema": { "type": "string" }, "required": true } ], "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "type": "object", "properties": { "webhook": { "$ref": "#/components/schemas/ReplyWebhook" }, "deliveries": { "type": "array", "items": { "$ref": "#/components/schemas/ReplyWebhookDelivery" } } }, "required": [ "webhook", "deliveries" ] } } } }, "400": { "description": "Invalid request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "401": { "description": "Missing, unknown, revoked, or expired token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "The token lacks a required scope or the account lacks permission", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "Not found for this account", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } ``` Resolve schema references against [OpenAPI](https://notify.belweave.ai/openapi.json). # Update a Reply webhook's endpoint, preset, headers, events, or enabled state PATCH /api/agent/webhooks/{id}. Required scopes: services:write. HTML: https://notify.belweave.ai/docs/api/patchWebhooksId Required scopes: `services:write` ```http PATCH /api/agent/webhooks/{id} Authorization: Bearer ntf_your_token ``` ## Operation schema ```json { "summary": "Update a Reply webhook's endpoint, preset, headers, events, or enabled state", "operationId": "patchWebhooksId", "security": [ { "bearer": [ "services:write" ] } ], "x-notify-scopes": [ "services:write" ], "parameters": [ { "name": "id", "in": "path", "schema": { "type": "string" }, "required": true } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "name": { "type": "string", "minLength": 1, "maxLength": 80 }, "url": { "type": "string", "maxLength": 2048, "format": "uri" }, "preset": { "type": "string", "enum": [ "generic", "grok", "slack" ] }, "headers": { "maxItems": 16, "type": "array", "items": { "type": "object", "properties": { "name": { "type": "string", "minLength": 1, "maxLength": 128, "pattern": "^[!#$%&'*+.^_`|~0-9A-Za-z-]+$" }, "value": { "type": "string", "minLength": 1, "maxLength": 4096 } }, "required": [ "name", "value" ] } }, "events": { "minItems": 1, "maxItems": 3, "type": "array", "items": { "type": "string", "enum": [ "interaction.answered", "interaction.expired", "interaction.canceled" ] } }, "enabled": { "type": "boolean" }, "isDefault": { "type": "boolean" } } } } } }, "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "type": "object", "properties": { "webhook": { "$ref": "#/components/schemas/ReplyWebhook" } }, "required": [ "webhook" ] } } } }, "400": { "description": "Invalid request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "401": { "description": "Missing, unknown, revoked, or expired token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "The token lacks a required scope or the account lacks permission", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "Not found for this account", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } ``` Resolve schema references against [OpenAPI](https://notify.belweave.ai/openapi.json). # Delete a Reply webhook DELETE /api/agent/webhooks/{id}. Required scopes: services:write. HTML: https://notify.belweave.ai/docs/api/deleteWebhooksId Required scopes: `services:write` ```http DELETE /api/agent/webhooks/{id} Authorization: Bearer ntf_your_token ``` ## Operation schema ```json { "summary": "Delete a Reply webhook", "operationId": "deleteWebhooksId", "security": [ { "bearer": [ "services:write" ] } ], "x-notify-scopes": [ "services:write" ], "parameters": [ { "name": "id", "in": "path", "schema": { "type": "string" }, "required": true } ], "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "type": "object", "properties": { "ok": { "const": true } }, "required": [ "ok" ] } } } }, "400": { "description": "Invalid request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "401": { "description": "Missing, unknown, revoked, or expired token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "The token lacks a required scope or the account lacks permission", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "Not found for this account", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } ``` Resolve schema references against [OpenAPI](https://notify.belweave.ai/openapi.json). # Send a signed sample event to a Reply webhook Makes an outbound HTTPS request using the saved endpoint, headers, and preset. HTML: https://notify.belweave.ai/docs/api/postWebhooksIdTest Required scopes: `services:write` ```http POST /api/agent/webhooks/{id}/test Authorization: Bearer ntf_your_token ``` ## Operation schema ```json { "summary": "Send a signed sample event to a Reply webhook", "description": "Makes an outbound HTTPS request using the saved endpoint, headers, and preset.", "operationId": "postWebhooksIdTest", "security": [ { "bearer": [ "services:write" ] } ], "x-notify-scopes": [ "services:write" ], "parameters": [ { "name": "id", "in": "path", "schema": { "type": "string" }, "required": true } ], "responses": { "202": { "description": "Success", "content": { "application/json": { "schema": { "type": "object", "properties": { "delivery": { "$ref": "#/components/schemas/ReplyWebhookDelivery" } }, "required": [ "delivery" ] } } } }, "400": { "description": "Invalid request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "401": { "description": "Missing, unknown, revoked, or expired token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "The token lacks a required scope or the account lacks permission", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "Not found for this account", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } ``` Resolve schema references against [OpenAPI](https://notify.belweave.ai/openapi.json). # Rotate a Reply webhook signing secret; the replacement is returned once Subsequent deliveries use the new secret. Update your receiver's signature verifier. HTML: https://notify.belweave.ai/docs/api/postWebhooksIdRotateSecret Required scopes: `services:write` ```http POST /api/agent/webhooks/{id}/rotate-secret Authorization: Bearer ntf_your_token ``` ## Operation schema ```json { "summary": "Rotate a Reply webhook signing secret; the replacement is returned once", "description": "Subsequent deliveries use the new secret. Update your receiver's signature verifier.", "operationId": "postWebhooksIdRotateSecret", "security": [ { "bearer": [ "services:write" ] } ], "x-notify-scopes": [ "services:write" ], "parameters": [ { "name": "id", "in": "path", "schema": { "type": "string" }, "required": true } ], "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ReplyWebhookCreated" } } } }, "400": { "description": "Invalid request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "401": { "description": "Missing, unknown, revoked, or expired token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "The token lacks a required scope or the account lacks permission", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "Not found for this account", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } ``` Resolve schema references against [OpenAPI](https://notify.belweave.ai/openapi.json). # Recent webhook deliveries GET /api/agent/events. Required scopes: events:read. HTML: https://notify.belweave.ai/docs/api/getEvents Required scopes: `events:read` ```http GET /api/agent/events Authorization: Bearer ntf_your_token ``` ## Operation schema ```json { "summary": "Recent webhook deliveries", "operationId": "getEvents", "security": [ { "bearer": [ "events:read" ] } ], "x-notify-scopes": [ "events:read" ], "parameters": [ { "name": "limit", "in": "query", "schema": { "type": "integer", "minimum": 1, "maximum": 100 }, "required": false } ], "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "type": "object", "properties": { "events": { "type": "array", "items": { "$ref": "#/components/schemas/Event" } } }, "required": [ "events" ] } } } }, "400": { "description": "Invalid request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "401": { "description": "Missing, unknown, revoked, or expired token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "The token lacks a required scope or the account lacks permission", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "Not found for this account", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } ``` Resolve schema references against [OpenAPI](https://notify.belweave.ai/openapi.json). # Merged account activity history, newest first (20 per page) GET /api/agent/activity-feed. Required scopes: events:read. HTML: https://notify.belweave.ai/docs/api/getActivityFeed Required scopes: `events:read` ```http GET /api/agent/activity-feed Authorization: Bearer ntf_your_token ``` ## Operation schema ```json { "summary": "Merged account activity history, newest first (20 per page)", "operationId": "getActivityFeed", "security": [ { "bearer": [ "events:read" ] } ], "x-notify-scopes": [ "events:read" ], "parameters": [ { "name": "filter", "in": "query", "schema": { "enum": [ "all", "notification", "live_activity", "response" ] }, "required": false }, { "name": "page", "in": "query", "schema": { "type": "integer", "minimum": 0 }, "required": false } ], "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ActivityFeedPage" } } } }, "400": { "description": "Invalid request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "401": { "description": "Missing, unknown, revoked, or expired token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "The token lacks a required scope or the account lacks permission", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "Not found for this account", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } ``` Resolve schema references against [OpenAPI](https://notify.belweave.ai/openapi.json). # Send a one-shot push With `oncall` (which also needs `oncall:write`), the push pages that on-call group instead (the token owner must be on its team) and returns the page; the Idempotency-Key becomes the page's dedup key. HTML: https://notify.belweave.ai/docs/api/postNotifications Required scopes: `notifications:send` ```http POST /api/agent/notifications Authorization: Bearer ntf_your_token ``` ## Operation schema ```json { "summary": "Send a one-shot push", "description": "With `oncall` (which also needs `oncall:write`), the push pages that on-call group instead (the token owner must be on its team) and returns the page; the Idempotency-Key becomes the page's dedup key.", "operationId": "postNotifications", "security": [ { "bearer": [ "notifications:send" ] } ], "x-notify-scopes": [ "notifications:send" ], "parameters": [ { "name": "Idempotency-Key", "in": "header", "schema": { "type": "string" }, "required": false } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "body": { "type": "string", "minLength": 1, "maxLength": 8000 }, "title": { "default": "Notify", "type": "string", "minLength": 1, "maxLength": 80 }, "imageUrl": { "type": "string", "maxLength": 2048, "format": "uri" }, "url": { "type": "string", "maxLength": 2048, "format": "uri" }, "deviceIds": { "minItems": 1, "maxItems": 50, "type": "array", "items": { "type": "string", "minLength": 1, "maxLength": 100 } }, "project": { "type": "string", "minLength": 1, "maxLength": 80 }, "summary": { "type": "string", "minLength": 1, "maxLength": 500 }, "bodyFormat": { "type": "string", "enum": [ "text", "markdown" ] }, "appId": { "type": "string", "pattern": "^app_[A-Za-z0-9_-]{8,64}$" }, "oncall": { "type": "string", "pattern": "^ocg_[A-Za-z0-9_-]{8,64}$" }, "group": { "type": "string", "pattern": "^dgrp_[A-Za-z0-9_-]{8,64}$" } }, "required": [ "body" ] } } } }, "responses": { "201": { "description": "Success", "content": { "application/json": { "schema": { "oneOf": [ { "$ref": "#/components/schemas/AgentNotificationCreated" }, { "$ref": "#/components/schemas/OncallPageCreated" } ] } } } }, "400": { "description": "Invalid request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "401": { "description": "Missing, unknown, revoked, or expired token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "The token lacks a required scope or the account lacks permission", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "Not found for this account", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } ``` Resolve schema references against [OpenAPI](https://notify.belweave.ai/openapi.json). # Withdraw an agent notification from the account's devices Sends a silent command that removes the push from Notification Center and marks the inbox copy read. Accepts `anot_…` or `notification:anot_…`. Returns 502 when no device accepted the command. HTML: https://notify.belweave.ai/docs/api/postNotificationsIdWithdraw Required scopes: `notifications:send` ```http POST /api/agent/notifications/{id}/withdraw Authorization: Bearer ntf_your_token ``` ## Operation schema ```json { "summary": "Withdraw an agent notification from the account's devices", "description": "Sends a silent command that removes the push from Notification Center and marks the inbox copy read. Accepts `anot_…` or `notification:anot_…`. Returns 502 when no device accepted the command.", "operationId": "postNotificationsIdWithdraw", "security": [ { "bearer": [ "notifications:send" ] } ], "x-notify-scopes": [ "notifications:send" ], "parameters": [ { "name": "id", "in": "path", "schema": { "type": "string" }, "required": true } ], "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AgentNotificationWithdrawn" } } } }, "400": { "description": "Invalid request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "401": { "description": "Missing, unknown, revoked, or expired token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "The token lacks a required scope or the account lacks permission", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "Not found for this account", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } ``` Resolve schema references against [OpenAPI](https://notify.belweave.ai/openapi.json). # List the account's pending prompts from every source There is deliberately no agent route that answers a prompt: approvals come only from a human on the phone or a signed-in session. HTML: https://notify.belweave.ai/docs/api/getInteractions Required scopes: `interactions:read` ```http GET /api/agent/interactions Authorization: Bearer ntf_your_token ``` ## Operation schema ```json { "summary": "List the account's pending prompts from every source", "description": "There is deliberately no agent route that answers a prompt: approvals come only from a human on the phone or a signed-in session.", "operationId": "getInteractions", "security": [ { "bearer": [ "interactions:read" ] } ], "x-notify-scopes": [ "interactions:read" ], "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "type": "object", "properties": { "interactions": { "type": "array", "items": { "$ref": "#/components/schemas/InboxInteraction" } } }, "required": [ "interactions" ] } } } }, "400": { "description": "Invalid request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "401": { "description": "Missing, unknown, revoked, or expired token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "The token lacks a required scope or the account lacks permission", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "Not found for this account", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } ``` Resolve schema references against [OpenAPI](https://notify.belweave.ai/openapi.json). # Ask a question (approval, yes/no, or reply) Set webhookId to a saved Reply webhook (whk_…) to receive answered, expired, or canceled events using its configured subscriptions. Live Activity presentations return immediately with accepted: 0 and queued device attempts; subsequent reads report provider acceptance. Queued work is not delivery or a human answer. HTML: https://notify.belweave.ai/docs/api/postInteractions Required scopes: `interactions:create`, `notifications:send` ```http POST /api/agent/interactions Authorization: Bearer ntf_your_token ``` ## Operation schema ```json { "summary": "Ask a question (approval, yes/no, or reply)", "description": "Set webhookId to a saved Reply webhook (whk_…) to receive answered, expired, or canceled events using its configured subscriptions. Live Activity presentations return immediately with accepted: 0 and queued device attempts; subsequent reads report provider acceptance. Queued work is not delivery or a human answer.", "operationId": "postInteractions", "security": [ { "bearer": [ "interactions:create", "notifications:send" ] } ], "x-notify-scopes": [ "interactions:create", "notifications:send" ], "parameters": [ { "name": "Idempotency-Key", "in": "header", "schema": { "type": "string" }, "required": false } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "title": { "type": "string", "minLength": 1, "maxLength": 80 }, "prompt": { "type": "string", "minLength": 1, "maxLength": 2000 }, "kind": { "type": "string", "enum": [ "approval", "yes_no", "reply" ] }, "imageUrl": { "type": "string", "maxLength": 2048, "format": "uri" }, "url": { "type": "string", "maxLength": 2048, "format": "uri" }, "deviceIds": { "minItems": 1, "maxItems": 50, "type": "array", "items": { "type": "string", "minLength": 1, "maxLength": 100 } }, "group": { "type": "string", "pattern": "^dgrp_[A-Za-z0-9_-]{8,64}$" }, "expiresInSeconds": { "default": 900, "type": "integer", "minimum": 30, "maximum": 86400 }, "presentation": { "type": "string", "enum": [ "notification", "live_activity" ] }, "style": { "type": "string", "enum": [ "approval", "shell", "verdict", "signal" ] }, "primaryLabel": { "type": "string", "minLength": 1, "maxLength": 24 }, "secondaryLabel": { "type": "string", "minLength": 1, "maxLength": 24 }, "callback": { "type": "object", "properties": { "url": { "type": "string", "maxLength": 2048, "format": "uri" }, "token": { "type": "string", "minLength": 16, "maxLength": 512 }, "target": { "type": "object", "properties": { "connectorId": { "type": "string", "pattern": "^conn_[A-Za-z0-9_-]{8,64}$" }, "route": { "type": "string", "pattern": "^(@|[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)$" }, "path": { "type": "string", "maxLength": 2048, "pattern": "^\\/[A-Za-z0-9._~!$&'()*+,;=:@%/-]*$" } }, "required": [ "connectorId", "route", "path" ] } } }, "webhookId": { "type": "string", "pattern": "^whk_[A-Za-z0-9_-]{1,100}$" } }, "required": [ "title", "prompt", "kind" ], "allOf": [ { "not": { "required": [ "group", "deviceIds" ] } }, { "if": { "properties": { "presentation": { "const": "live_activity" } }, "required": [ "presentation" ] }, "then": { "properties": { "kind": { "enum": [ "approval", "yes_no" ] }, "prompt": { "maxLength": 240 }, "expiresInSeconds": { "maximum": 28800 } }, "not": { "anyOf": [ { "required": [ "url" ] }, { "required": [ "imageUrl" ] } ] } }, "else": { "not": { "anyOf": [ { "required": [ "style" ] }, { "required": [ "primaryLabel" ] }, { "required": [ "secondaryLabel" ] } ] } } } ] } } } }, "responses": { "201": { "description": "Success", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/InteractionCreated" } } } }, "400": { "description": "Invalid request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "401": { "description": "Missing, unknown, revoked, or expired token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "The token lacks a required scope or the account lacks permission", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "Not found for this account", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } ``` Resolve schema references against [OpenAPI](https://notify.belweave.ai/openapi.json). # Get a prompt created by this token GET /api/agent/interactions/{id}. Required scopes: interactions:read. HTML: https://notify.belweave.ai/docs/api/getInteractionsId Required scopes: `interactions:read` ```http GET /api/agent/interactions/{id} Authorization: Bearer ntf_your_token ``` ## Operation schema ```json { "summary": "Get a prompt created by this token", "operationId": "getInteractionsId", "security": [ { "bearer": [ "interactions:read" ] } ], "x-notify-scopes": [ "interactions:read" ], "parameters": [ { "name": "id", "in": "path", "schema": { "type": "string" }, "required": true } ], "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "type": "object", "properties": { "interaction": { "$ref": "#/components/schemas/Interaction" } }, "required": [ "interaction" ] } } } }, "400": { "description": "Invalid request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "401": { "description": "Missing, unknown, revoked, or expired token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "The token lacks a required scope or the account lacks permission", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "Not found for this account", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } ``` Resolve schema references against [OpenAPI](https://notify.belweave.ai/openapi.json). # Long-poll a prompt created by this token until it is answered GET /api/agent/interactions/{id}/wait. Required scopes: interactions:read. HTML: https://notify.belweave.ai/docs/api/getInteractionsIdWait Required scopes: `interactions:read` ```http GET /api/agent/interactions/{id}/wait Authorization: Bearer ntf_your_token ``` ## Operation schema ```json { "summary": "Long-poll a prompt created by this token until it is answered", "operationId": "getInteractionsIdWait", "security": [ { "bearer": [ "interactions:read" ] } ], "x-notify-scopes": [ "interactions:read" ], "parameters": [ { "name": "id", "in": "path", "schema": { "type": "string" }, "required": true }, { "name": "timeout", "in": "query", "schema": { "type": "number", "minimum": 0, "maximum": 25 }, "required": false } ], "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "type": "object", "properties": { "interaction": { "$ref": "#/components/schemas/Interaction" }, "timedOut": { "type": "boolean" } }, "required": [ "interaction", "timedOut" ] } } } }, "400": { "description": "Invalid request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "401": { "description": "Missing, unknown, revoked, or expired token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "The token lacks a required scope or the account lacks permission", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "Not found for this account", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } ``` Resolve schema references against [OpenAPI](https://notify.belweave.ai/openapi.json). # Cancel a pending prompt created by this token POST /api/agent/interactions/{id}/cancel. Required scopes: interactions:create. HTML: https://notify.belweave.ai/docs/api/postInteractionsIdCancel Required scopes: `interactions:create` ```http POST /api/agent/interactions/{id}/cancel Authorization: Bearer ntf_your_token ``` ## Operation schema ```json { "summary": "Cancel a pending prompt created by this token", "operationId": "postInteractionsIdCancel", "security": [ { "bearer": [ "interactions:create" ] } ], "x-notify-scopes": [ "interactions:create" ], "parameters": [ { "name": "id", "in": "path", "schema": { "type": "string" }, "required": true } ], "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "type": "object", "properties": { "interaction": { "$ref": "#/components/schemas/Interaction" } }, "required": [ "interaction" ] } } } }, "400": { "description": "Invalid request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "401": { "description": "Missing, unknown, revoked, or expired token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "The token lacks a required scope or the account lacks permission", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "Not found for this account", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } ``` Resolve schema references against [OpenAPI](https://notify.belweave.ai/openapi.json). # List Live Activities started by this token GET /api/agent/activities. Required scopes: activities:read. HTML: https://notify.belweave.ai/docs/api/getActivities Required scopes: `activities:read` ```http GET /api/agent/activities Authorization: Bearer ntf_your_token ``` ## Operation schema ```json { "summary": "List Live Activities started by this token", "operationId": "getActivities", "security": [ { "bearer": [ "activities:read" ] } ], "x-notify-scopes": [ "activities:read" ], "parameters": [ { "name": "limit", "in": "query", "schema": { "type": "integer", "minimum": 1, "maximum": 100 }, "required": false } ], "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "type": "object", "properties": { "activities": { "type": "array", "items": { "$ref": "#/components/schemas/LiveActivity" } } }, "required": [ "activities" ] } } } }, "400": { "description": "Invalid request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "401": { "description": "Missing, unknown, revoked, or expired token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "The token lacks a required scope or the account lacks permission", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "Not found for this account", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } ``` Resolve schema references against [OpenAPI](https://notify.belweave.ai/openapi.json). # Start a Live Activity Up to five concurrent activities per device, including interactive activities. Distinct keys coexist. An active key owned by this requester conflicts unless replace is true; replacement affects only that key. Device capacity returns 422 ACTIVITY_CAPACITY_EXCEEDED with limit: 5. Suggested visible-character budgets, not guaranteed fit or API hard limits: proportional glyphs, device width and accessibility text size change fit. 0 means hidden. Expanded and Lock Screen subtitle replaces status on line 2; compact and small banners retain status. Omitted subtitle uses status. Overflow shares the two-line detail area with explicit detail. Compact percentage takes precedence over status when progress is supplied; terminal shows percentage or its prompt icon and never status. Expanded budgets (title/line 2/detail): standard 32/36/100; ring 32/30/80; hero 24/36/100; terminal 36/40/100; steps 32/36/100. Compact title/subtitle/detail are hidden; status budget is 8 without progress, except terminal 0. HTML: https://notify.belweave.ai/docs/api/postActivities Required scopes: `activities:write` ```http POST /api/agent/activities Authorization: Bearer ntf_your_token ``` ## Operation schema ```json { "summary": "Start a Live Activity", "description": "Up to five concurrent activities per device, including interactive activities. Distinct keys coexist. An active key owned by this requester conflicts unless replace is true; replacement affects only that key. Device capacity returns 422 ACTIVITY_CAPACITY_EXCEEDED with limit: 5. Suggested visible-character budgets, not guaranteed fit or API hard limits: proportional glyphs, device width and accessibility text size change fit. 0 means hidden. Expanded and Lock Screen subtitle replaces status on line 2; compact and small banners retain status. Omitted subtitle uses status. Overflow shares the two-line detail area with explicit detail. Compact percentage takes precedence over status when progress is supplied; terminal shows percentage or its prompt icon and never status. Expanded budgets (title/line 2/detail): standard 32/36/100; ring 32/30/80; hero 24/36/100; terminal 36/40/100; steps 32/36/100. Compact title/subtitle/detail are hidden; status budget is 8 without progress, except terminal 0.", "operationId": "postActivities", "security": [ { "bearer": [ "activities:write" ] } ], "x-notify-scopes": [ "activities:write" ], "parameters": [ { "name": "Idempotency-Key", "in": "header", "schema": { "type": "string" }, "required": false } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "key": { "type": "string", "minLength": 1, "maxLength": 100 }, "replace": { "default": false, "type": "boolean" }, "title": { "type": "string", "minLength": 1, "maxLength": 80, "description": "Task title on line 1. Suggested character budgets: standard: compact 0, expanded 32; ring: compact 0, expanded 32; hero: compact 0, expanded 24; terminal: compact 0, expanded 36; steps: compact 0, expanded 32. Suggested visible-character budgets, not guaranteed fit or API hard limits: proportional glyphs, device width and accessibility text size change fit. 0 means hidden. Expanded and Lock Screen subtitle replaces status on line 2; compact and small banners retain status. Omitted subtitle uses status. Overflow shares the two-line detail area with explicit detail. Compact percentage takes precedence over status when progress is supplied; terminal shows percentage or its prompt icon and never status." }, "subtitle": { "type": "string", "minLength": 1, "maxLength": 120, "description": "Optional full-word summary used instead of status on expanded Dynamic Island and Lock Screen line 2 only; compact and small banners retain status. Omit to use status; null clears it on update/end. Suggested character budgets: standard: compact 0, expanded 36; ring: compact 0, expanded 30; hero: compact 0, expanded 36; terminal: compact 0, expanded 40; steps: compact 0, expanded 36. Suggested visible-character budgets, not guaranteed fit or API hard limits: proportional glyphs, device width and accessibility text size change fit. 0 means hidden. Expanded and Lock Screen subtitle replaces status on line 2; compact and small banners retain status. Omitted subtitle uses status. Overflow shares the two-line detail area with explicit detail. Compact percentage takes precedence over status when progress is supplied; terminal shows percentage or its prompt icon and never status." }, "status": { "type": "string", "minLength": 1, "maxLength": 60, "description": "Required task state; displayed on line 2 when subtitle is absent. Full status remains available to accessibility. Suggested character budgets: standard: compact 8, expanded 36; ring: compact 8, expanded 30; hero: compact 8, expanded 36; terminal: compact 0, expanded 40; steps: compact 8, expanded 36. Suggested visible-character budgets, not guaranteed fit or API hard limits: proportional glyphs, device width and accessibility text size change fit. 0 means hidden. Expanded and Lock Screen subtitle replaces status on line 2; compact and small banners retain status. Omitted subtitle uses status. Overflow shares the two-line detail area with explicit detail. Compact percentage takes precedence over status when progress is supplied; terminal shows percentage or its prompt icon and never status." }, "detail": { "type": "string", "minLength": 1, "maxLength": 240, "description": "Additional context in the two-line detail area, sharing space with line 2 overflow. Suggested character budgets: standard: compact 0, expanded 100; ring: compact 0, expanded 80; hero: compact 0, expanded 100; terminal: compact 0, expanded 100; steps: compact 0, expanded 100. Suggested visible-character budgets, not guaranteed fit or API hard limits: proportional glyphs, device width and accessibility text size change fit. 0 means hidden. Expanded and Lock Screen subtitle replaces status on line 2; compact and small banners retain status. Omitted subtitle uses status. Overflow shares the two-line detail area with explicit detail. Compact percentage takes precedence over status when progress is supplied; terminal shows percentage or its prompt icon and never status." }, "progress": { "type": "number", "minimum": 0, "maximum": 1 }, "symbol": { "default": "terminal", "type": "string", "enum": [ "terminal", "code", "build", "success", "warning" ] }, "privacyMode": { "default": "standard", "type": "string", "enum": [ "standard", "private" ] }, "accentColor": { "default": "#246BEB", "type": "string", "pattern": "^#[0-9a-fA-F]{6}$" }, "style": { "default": "standard", "type": "string", "enum": [ "standard", "ring", "hero", "terminal", "steps" ] }, "deviceIds": { "minItems": 1, "maxItems": 50, "type": "array", "items": { "type": "string", "minLength": 1, "maxLength": 100 } }, "group": { "type": "string", "pattern": "^dgrp_[A-Za-z0-9_-]{8,64}$" }, "expiresInSeconds": { "default": 28800, "type": "integer", "minimum": 60, "maximum": 28800 }, "staleAfterSeconds": { "default": 14400, "type": "integer", "minimum": 0, "maximum": 28800 } }, "required": [ "title", "status" ] } } } }, "responses": { "201": { "description": "Success", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/LiveActivityMutation" } } } }, "400": { "description": "Invalid request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "401": { "description": "Missing, unknown, revoked, or expired token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "The token lacks a required scope or the account lacks permission", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "Not found for this account", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } ``` Resolve schema references against [OpenAPI](https://notify.belweave.ai/openapi.json). # Get a Live Activity by ID or key GET /api/agent/activities/{identifier}. Required scopes: activities:read. HTML: https://notify.belweave.ai/docs/api/getActivitiesIdentifier Required scopes: `activities:read` ```http GET /api/agent/activities/{identifier} Authorization: Bearer ntf_your_token ``` ## Operation schema ```json { "summary": "Get a Live Activity by ID or key", "operationId": "getActivitiesIdentifier", "security": [ { "bearer": [ "activities:read" ] } ], "x-notify-scopes": [ "activities:read" ], "parameters": [ { "name": "identifier", "in": "path", "schema": { "type": "string" }, "required": true } ], "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "type": "object", "properties": { "activity": { "$ref": "#/components/schemas/LiveActivity" } }, "required": [ "activity" ] } } } }, "400": { "description": "Invalid request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "401": { "description": "Missing, unknown, revoked, or expired token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "The token lacks a required scope or the account lacks permission", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "Not found for this account", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } ``` Resolve schema references against [OpenAPI](https://notify.belweave.ai/openapi.json). # Update a Live Activity Use priority: "high" for user-visible milestones (APNs 10), bounded to 4 per rolling minute and 20 per rolling hour per activity. Excess requests use APNs 5 with priorityReason: "budget_exhausted". Granted milestones remain queue barriers. Default routine updates use APNs 5. These are Notify admission limits, not Apple's unpublished device budget; iOS may still delay or skip states. Updates are accepted durably before an iOS update token is registered, and transient provider rejection remains queued for retry. queued counts pending device operations separately from accepted provider requests and permanent failures. Pending operations expire at the original activity deadline. Suggested visible-character budgets, not guaranteed fit or API hard limits: proportional glyphs, device width and accessibility text size change fit. 0 means hidden. Expanded and Lock Screen subtitle replaces status on line 2; compact and small banners retain status. Omitted subtitle uses status. Overflow shares the two-line detail area with explicit detail. Compact percentage takes precedence over status when progress is supplied; terminal shows percentage or its prompt icon and never status. Expanded budgets (title/line 2/detail): standard 32/36/100; ring 32/30/80; hero 24/36/100; terminal 36/40/100; steps 32/36/100. Compact title/subtitle/detail are hidden; status budget is 8 without progress, except terminal 0. HTML: https://notify.belweave.ai/docs/api/patchActivitiesIdentifier Required scopes: `activities:write` ```http PATCH /api/agent/activities/{identifier} Authorization: Bearer ntf_your_token ``` ## Operation schema ```json { "summary": "Update a Live Activity", "description": "Use priority: \"high\" for user-visible milestones (APNs 10), bounded to 4 per rolling minute and 20 per rolling hour per activity. Excess requests use APNs 5 with priorityReason: \"budget_exhausted\". Granted milestones remain queue barriers. Default routine updates use APNs 5. These are Notify admission limits, not Apple's unpublished device budget; iOS may still delay or skip states. Updates are accepted durably before an iOS update token is registered, and transient provider rejection remains queued for retry. queued counts pending device operations separately from accepted provider requests and permanent failures. Pending operations expire at the original activity deadline. Suggested visible-character budgets, not guaranteed fit or API hard limits: proportional glyphs, device width and accessibility text size change fit. 0 means hidden. Expanded and Lock Screen subtitle replaces status on line 2; compact and small banners retain status. Omitted subtitle uses status. Overflow shares the two-line detail area with explicit detail. Compact percentage takes precedence over status when progress is supplied; terminal shows percentage or its prompt icon and never status. Expanded budgets (title/line 2/detail): standard 32/36/100; ring 32/30/80; hero 24/36/100; terminal 36/40/100; steps 32/36/100. Compact title/subtitle/detail are hidden; status budget is 8 without progress, except terminal 0.", "operationId": "patchActivitiesIdentifier", "security": [ { "bearer": [ "activities:write" ] } ], "x-notify-scopes": [ "activities:write" ], "parameters": [ { "name": "identifier", "in": "path", "schema": { "type": "string" }, "required": true }, { "name": "Idempotency-Key", "in": "header", "schema": { "type": "string" }, "required": false } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "title": { "type": "string", "minLength": 1, "maxLength": 80, "description": "Task title on line 1. Suggested character budgets: standard: compact 0, expanded 32; ring: compact 0, expanded 32; hero: compact 0, expanded 24; terminal: compact 0, expanded 36; steps: compact 0, expanded 32. Suggested visible-character budgets, not guaranteed fit or API hard limits: proportional glyphs, device width and accessibility text size change fit. 0 means hidden. Expanded and Lock Screen subtitle replaces status on line 2; compact and small banners retain status. Omitted subtitle uses status. Overflow shares the two-line detail area with explicit detail. Compact percentage takes precedence over status when progress is supplied; terminal shows percentage or its prompt icon and never status." }, "subtitle": { "anyOf": [ { "type": "string", "minLength": 1, "maxLength": 120, "description": "Optional full-word summary used instead of status on expanded Dynamic Island and Lock Screen line 2 only; compact and small banners retain status. Omit to use status; null clears it on update/end. Suggested character budgets: standard: compact 0, expanded 36; ring: compact 0, expanded 30; hero: compact 0, expanded 36; terminal: compact 0, expanded 40; steps: compact 0, expanded 36. Suggested visible-character budgets, not guaranteed fit or API hard limits: proportional glyphs, device width and accessibility text size change fit. 0 means hidden. Expanded and Lock Screen subtitle replaces status on line 2; compact and small banners retain status. Omitted subtitle uses status. Overflow shares the two-line detail area with explicit detail. Compact percentage takes precedence over status when progress is supplied; terminal shows percentage or its prompt icon and never status." }, { "type": "null" } ] }, "status": { "type": "string", "minLength": 1, "maxLength": 60, "description": "Required task state; displayed on line 2 when subtitle is absent. Full status remains available to accessibility. Suggested character budgets: standard: compact 8, expanded 36; ring: compact 8, expanded 30; hero: compact 8, expanded 36; terminal: compact 0, expanded 40; steps: compact 8, expanded 36. Suggested visible-character budgets, not guaranteed fit or API hard limits: proportional glyphs, device width and accessibility text size change fit. 0 means hidden. Expanded and Lock Screen subtitle replaces status on line 2; compact and small banners retain status. Omitted subtitle uses status. Overflow shares the two-line detail area with explicit detail. Compact percentage takes precedence over status when progress is supplied; terminal shows percentage or its prompt icon and never status." }, "detail": { "anyOf": [ { "type": "string", "minLength": 1, "maxLength": 240, "description": "Additional context in the two-line detail area, sharing space with line 2 overflow. Suggested character budgets: standard: compact 0, expanded 100; ring: compact 0, expanded 80; hero: compact 0, expanded 100; terminal: compact 0, expanded 100; steps: compact 0, expanded 100. Suggested visible-character budgets, not guaranteed fit or API hard limits: proportional glyphs, device width and accessibility text size change fit. 0 means hidden. Expanded and Lock Screen subtitle replaces status on line 2; compact and small banners retain status. Omitted subtitle uses status. Overflow shares the two-line detail area with explicit detail. Compact percentage takes precedence over status when progress is supplied; terminal shows percentage or its prompt icon and never status." }, { "type": "null" } ] }, "progress": { "anyOf": [ { "type": "number", "minimum": 0, "maximum": 1 }, { "type": "null" } ] }, "symbol": { "type": "string", "enum": [ "terminal", "code", "build", "success", "warning" ] }, "privacyMode": { "type": "string", "enum": [ "standard", "private" ] }, "accentColor": { "type": "string", "pattern": "^#[0-9a-fA-F]{6}$" }, "style": { "type": "string", "enum": [ "standard", "ring", "hero", "terminal", "steps" ] }, "staleAfterSeconds": { "type": "integer", "minimum": 0, "maximum": 28800 }, "priority": { "description": "Use high for a user-visible milestone (APNs 10). Limited to 4 per rolling minute and 20 per rolling hour per activity; excess requests use APNs 5. Routine updates default to normal (5). iOS may still delay or skip states.", "type": "string", "enum": [ "normal", "high" ] }, "ifSequence": { "type": "integer", "minimum": 0, "maximum": 9007199254740991 } } } } } }, "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/LiveActivityMutation" } } } }, "400": { "description": "Invalid request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "401": { "description": "Missing, unknown, revoked, or expired token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "The token lacks a required scope or the account lacks permission", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "Not found for this account", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } ``` Resolve schema references against [OpenAPI](https://notify.belweave.ai/openapi.json). # End a Live Activity Ends the logical activity immediately and accepts device end operations durably, including before an iOS update token arrives. HTTP 200 may have accepted: 0, failed: 0, queued: N. Late token registration flushes pending operations in sequence; queued is not provider acceptance or confirmed device removal. Suggested visible-character budgets, not guaranteed fit or API hard limits: proportional glyphs, device width and accessibility text size change fit. 0 means hidden. Expanded and Lock Screen subtitle replaces status on line 2; compact and small banners retain status. Omitted subtitle uses status. Overflow shares the two-line detail area with explicit detail. Compact percentage takes precedence over status when progress is supplied; terminal shows percentage or its prompt icon and never status. Expanded budgets (title/line 2/detail): standard 32/36/100; ring 32/30/80; hero 24/36/100; terminal 36/40/100; steps 32/36/100. Compact title/subtitle/detail are hidden; status budget is 8 without progress, except terminal 0. HTML: https://notify.belweave.ai/docs/api/postActivitiesIdentifierEnd Required scopes: `activities:write` ```http POST /api/agent/activities/{identifier}/end Authorization: Bearer ntf_your_token ``` ## Operation schema ```json { "summary": "End a Live Activity", "description": "Ends the logical activity immediately and accepts device end operations durably, including before an iOS update token arrives. HTTP 200 may have accepted: 0, failed: 0, queued: N. Late token registration flushes pending operations in sequence; queued is not provider acceptance or confirmed device removal. Suggested visible-character budgets, not guaranteed fit or API hard limits: proportional glyphs, device width and accessibility text size change fit. 0 means hidden. Expanded and Lock Screen subtitle replaces status on line 2; compact and small banners retain status. Omitted subtitle uses status. Overflow shares the two-line detail area with explicit detail. Compact percentage takes precedence over status when progress is supplied; terminal shows percentage or its prompt icon and never status. Expanded budgets (title/line 2/detail): standard 32/36/100; ring 32/30/80; hero 24/36/100; terminal 36/40/100; steps 32/36/100. Compact title/subtitle/detail are hidden; status budget is 8 without progress, except terminal 0.", "operationId": "postActivitiesIdentifierEnd", "security": [ { "bearer": [ "activities:write" ] } ], "x-notify-scopes": [ "activities:write" ], "parameters": [ { "name": "identifier", "in": "path", "schema": { "type": "string" }, "required": true }, { "name": "Idempotency-Key", "in": "header", "schema": { "type": "string" }, "required": false } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "subtitle": { "anyOf": [ { "type": "string", "minLength": 1, "maxLength": 120, "description": "Optional full-word summary used instead of status on expanded Dynamic Island and Lock Screen line 2 only; compact and small banners retain status. Omit to use status; null clears it on update/end. Suggested character budgets: standard: compact 0, expanded 36; ring: compact 0, expanded 30; hero: compact 0, expanded 36; terminal: compact 0, expanded 40; steps: compact 0, expanded 36. Suggested visible-character budgets, not guaranteed fit or API hard limits: proportional glyphs, device width and accessibility text size change fit. 0 means hidden. Expanded and Lock Screen subtitle replaces status on line 2; compact and small banners retain status. Omitted subtitle uses status. Overflow shares the two-line detail area with explicit detail. Compact percentage takes precedence over status when progress is supplied; terminal shows percentage or its prompt icon and never status." }, { "type": "null" } ] }, "status": { "default": "Complete", "type": "string", "minLength": 1, "maxLength": 60, "description": "Required task state; displayed on line 2 when subtitle is absent. Full status remains available to accessibility. Suggested character budgets: standard: compact 8, expanded 36; ring: compact 8, expanded 30; hero: compact 8, expanded 36; terminal: compact 0, expanded 40; steps: compact 8, expanded 36. Suggested visible-character budgets, not guaranteed fit or API hard limits: proportional glyphs, device width and accessibility text size change fit. 0 means hidden. Expanded and Lock Screen subtitle replaces status on line 2; compact and small banners retain status. Omitted subtitle uses status. Overflow shares the two-line detail area with explicit detail. Compact percentage takes precedence over status when progress is supplied; terminal shows percentage or its prompt icon and never status." }, "detail": { "anyOf": [ { "type": "string", "minLength": 1, "maxLength": 240, "description": "Additional context in the two-line detail area, sharing space with line 2 overflow. Suggested character budgets: standard: compact 0, expanded 100; ring: compact 0, expanded 80; hero: compact 0, expanded 100; terminal: compact 0, expanded 100; steps: compact 0, expanded 100. Suggested visible-character budgets, not guaranteed fit or API hard limits: proportional glyphs, device width and accessibility text size change fit. 0 means hidden. Expanded and Lock Screen subtitle replaces status on line 2; compact and small banners retain status. Omitted subtitle uses status. Overflow shares the two-line detail area with explicit detail. Compact percentage takes precedence over status when progress is supplied; terminal shows percentage or its prompt icon and never status." }, { "type": "null" } ] }, "progress": { "anyOf": [ { "type": "number", "minimum": 0, "maximum": 1 }, { "type": "null" } ] }, "symbol": { "default": "success", "type": "string", "enum": [ "terminal", "code", "build", "success", "warning" ] }, "accentColor": { "type": "string", "pattern": "^#[0-9a-fA-F]{6}$" }, "dismissAfterSeconds": { "default": 0, "type": "integer", "minimum": 0, "maximum": 14400 }, "ifSequence": { "type": "integer", "minimum": 0, "maximum": 9007199254740991 } } } } } }, "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/LiveActivityMutation" } } } }, "400": { "description": "Invalid request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "401": { "description": "Missing, unknown, revoked, or expired token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "The token lacks a required scope or the account lacks permission", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "Not found for this account", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "409": { "description": "Sequence conflict or activity already terminal" } } } ``` Resolve schema references against [OpenAPI](https://notify.belweave.ai/openapi.json). # List web apps GET /api/agent/apps. Required scopes: apps:read. HTML: https://notify.belweave.ai/docs/api/getApps Required scopes: `apps:read` ```http GET /api/agent/apps Authorization: Bearer ntf_your_token ``` ## Operation schema ```json { "summary": "List web apps", "operationId": "getApps", "security": [ { "bearer": [ "apps:read" ] } ], "x-notify-scopes": [ "apps:read" ], "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "type": "object", "properties": { "apps": { "type": "array", "items": { "$ref": "#/components/schemas/App" } } }, "required": [ "apps" ] } } } }, "400": { "description": "Invalid request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "401": { "description": "Missing, unknown, revoked, or expired token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "The token lacks a required scope or the account lacks permission", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "Not found for this account", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } ``` Resolve schema references against [OpenAPI](https://notify.belweave.ai/openapi.json). # Register a web app (an existing URL is updated) POST /api/agent/apps. Required scopes: apps:write. HTML: https://notify.belweave.ai/docs/api/postApps Required scopes: `apps:write` ```http POST /api/agent/apps Authorization: Bearer ntf_your_token ``` ## Operation schema ```json { "summary": "Register a web app (an existing URL is updated)", "operationId": "postApps", "security": [ { "bearer": [ "apps:write" ] } ], "x-notify-scopes": [ "apps:write" ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "name": { "type": "string", "minLength": 1, "maxLength": 40 }, "url": { "type": "string", "maxLength": 2048, "format": "uri" }, "iconUrl": { "type": "string", "maxLength": 2048, "format": "uri" }, "project": { "type": "string", "minLength": 1, "maxLength": 80 }, "teamId": { "type": "string", "pattern": "^team_[A-Za-z0-9_-]{8,64}$", "description": "Team or free Family ID (team_…) returned by teams_create or teams_list" } }, "required": [ "name", "url" ] } } } }, "responses": { "201": { "description": "Success", "content": { "application/json": { "schema": { "type": "object", "properties": { "app": { "$ref": "#/components/schemas/App" }, "created": { "type": "boolean" }, "message": { "type": "string" } }, "required": [ "app", "created" ] } } } }, "400": { "description": "Invalid request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "401": { "description": "Missing, unknown, revoked, or expired token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "The token lacks a required scope or the account lacks permission", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "Not found for this account", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } ``` Resolve schema references against [OpenAPI](https://notify.belweave.ai/openapi.json). # Get a web app GET /api/agent/apps/{id}. Required scopes: apps:read. HTML: https://notify.belweave.ai/docs/api/getAppsId Required scopes: `apps:read` ```http GET /api/agent/apps/{id} Authorization: Bearer ntf_your_token ``` ## Operation schema ```json { "summary": "Get a web app", "operationId": "getAppsId", "security": [ { "bearer": [ "apps:read" ] } ], "x-notify-scopes": [ "apps:read" ], "parameters": [ { "name": "id", "in": "path", "schema": { "type": "string" }, "required": true } ], "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "type": "object", "properties": { "app": { "$ref": "#/components/schemas/App" } }, "required": [ "app" ] } } } }, "400": { "description": "Invalid request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "401": { "description": "Missing, unknown, revoked, or expired token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "The token lacks a required scope or the account lacks permission", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "Not found for this account", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } ``` Resolve schema references against [OpenAPI](https://notify.belweave.ai/openapi.json). # Update a web app's name, URL, icon, or project Personal name/email disclosure preferences and sign-in consent require a signed-in session and cannot be changed here. Moving the URL to a new origin clears consent. HTML: https://notify.belweave.ai/docs/api/patchAppsId Required scopes: `apps:write` ```http PATCH /api/agent/apps/{id} Authorization: Bearer ntf_your_token ``` ## Operation schema ```json { "summary": "Update a web app's name, URL, icon, or project", "description": "Personal name/email disclosure preferences and sign-in consent require a signed-in session and cannot be changed here. Moving the URL to a new origin clears consent.", "operationId": "patchAppsId", "security": [ { "bearer": [ "apps:write" ] } ], "x-notify-scopes": [ "apps:write" ], "parameters": [ { "name": "id", "in": "path", "schema": { "type": "string" }, "required": true } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "name": { "type": "string", "minLength": 1, "maxLength": 40 }, "url": { "type": "string", "maxLength": 2048, "format": "uri" }, "iconUrl": { "anyOf": [ { "type": "string", "maxLength": 2048, "format": "uri" }, { "type": "null" } ] }, "project": { "anyOf": [ { "type": "string", "minLength": 1, "maxLength": 80 }, { "type": "null" } ] } }, "additionalProperties": false } } } }, "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "type": "object", "properties": { "app": { "$ref": "#/components/schemas/App" }, "message": { "type": "string" } }, "required": [ "app" ] } } } }, "400": { "description": "Invalid request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "401": { "description": "Missing, unknown, revoked, or expired token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "The token lacks a required scope or the account lacks permission", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "Not found for this account", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } ``` Resolve schema references against [OpenAPI](https://notify.belweave.ai/openapi.json). # Remove a web app DELETE /api/agent/apps/{id}. Required scopes: apps:write. HTML: https://notify.belweave.ai/docs/api/deleteAppsId Required scopes: `apps:write` ```http DELETE /api/agent/apps/{id} Authorization: Bearer ntf_your_token ``` ## Operation schema ```json { "summary": "Remove a web app", "operationId": "deleteAppsId", "security": [ { "bearer": [ "apps:write" ] } ], "x-notify-scopes": [ "apps:write" ], "parameters": [ { "name": "id", "in": "path", "schema": { "type": "string" }, "required": true } ], "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "type": "object", "properties": { "ok": { "const": true } }, "required": [ "ok" ] } } } }, "400": { "description": "Invalid request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "401": { "description": "Missing, unknown, revoked, or expired token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "The token lacks a required scope or the account lacks permission", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "Not found for this account", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } ``` Resolve schema references against [OpenAPI](https://notify.belweave.ai/openapi.json). # Revoke this account's sign-in consent; approve again on next open Agents can revoke the calling account's consent but cannot grant consent or issue Notify passes. Those actions require a signed-in session. HTML: https://notify.belweave.ai/docs/api/postAppsIdRevoke Required scopes: `apps:write` ```http POST /api/agent/apps/{id}/revoke Authorization: Bearer ntf_your_token ``` ## Operation schema ```json { "summary": "Revoke this account's sign-in consent; approve again on next open", "description": "Agents can revoke the calling account's consent but cannot grant consent or issue Notify passes. Those actions require a signed-in session.", "operationId": "postAppsIdRevoke", "security": [ { "bearer": [ "apps:write" ] } ], "x-notify-scopes": [ "apps:write" ], "parameters": [ { "name": "id", "in": "path", "schema": { "type": "string" }, "required": true } ], "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "type": "object", "properties": { "app": { "$ref": "#/components/schemas/App" } }, "required": [ "app" ] } } } }, "400": { "description": "Invalid request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "401": { "description": "Missing, unknown, revoked, or expired token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "The token lacks a required scope or the account lacks permission", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "Not found for this account", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } ``` Resolve schema references against [OpenAPI](https://notify.belweave.ai/openapi.json). # Move an app into a team or family, or (teamId null) back to your own apps Only the person who added the app can move it. Other members are notified unless `notify` is false, and each approves sign-in for themselves. HTML: https://notify.belweave.ai/docs/api/postAppsIdShare Required scopes: `apps:write` ```http POST /api/agent/apps/{id}/share Authorization: Bearer ntf_your_token ``` ## Operation schema ```json { "summary": "Move an app into a team or family, or (teamId null) back to your own apps", "description": "Only the person who added the app can move it. Other members are notified unless `notify` is false, and each approves sign-in for themselves.", "operationId": "postAppsIdShare", "security": [ { "bearer": [ "apps:write" ] } ], "x-notify-scopes": [ "apps:write" ], "parameters": [ { "name": "id", "in": "path", "schema": { "type": "string" }, "required": true } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "teamId": { "anyOf": [ { "type": "string", "pattern": "^team_[A-Za-z0-9_-]{8,64}$", "description": "Team or free Family ID (team_…) returned by teams_create or teams_list" }, { "type": "null" } ] }, "notify": { "type": "boolean" } }, "required": [ "teamId" ], "additionalProperties": false } } } }, "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "type": "object", "properties": { "app": { "$ref": "#/components/schemas/App" } }, "required": [ "app" ] } } } }, "400": { "description": "Invalid request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "401": { "description": "Missing, unknown, revoked, or expired token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "The token lacks a required scope or the account lacks permission", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "Not found for this account", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } ``` Resolve schema references against [OpenAPI](https://notify.belweave.ai/openapi.json). # List the teams you belong to GET /api/agent/teams. Required scopes: teams:read. HTML: https://notify.belweave.ai/docs/api/getTeams Required scopes: `teams:read` ```http GET /api/agent/teams Authorization: Bearer ntf_your_token ``` ## Operation schema ```json { "summary": "List the teams you belong to", "operationId": "getTeams", "security": [ { "bearer": [ "teams:read" ] } ], "x-notify-scopes": [ "teams:read" ], "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "type": "object", "properties": { "teams": { "type": "array", "items": { "$ref": "#/components/schemas/Team" } } }, "required": [ "teams" ] } } } }, "400": { "description": "Invalid request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "401": { "description": "Missing, unknown, revoked, or expired token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "The token lacks a required scope or the account lacks permission", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "Not found for this account", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } ``` Resolve schema references against [OpenAPI](https://notify.belweave.ai/openapi.json). # Create a family; you become its owner Use kind "family". New Teams creation (kind "team" or omitted) returns 403; existing teams continue operating. HTML: https://notify.belweave.ai/docs/api/postTeams Required scopes: `teams:write` ```http POST /api/agent/teams Authorization: Bearer ntf_your_token ``` ## Operation schema ```json { "summary": "Create a family; you become its owner", "description": "Use kind \"family\". New Teams creation (kind \"team\" or omitted) returns 403; existing teams continue operating.", "operationId": "postTeams", "security": [ { "bearer": [ "teams:write" ] } ], "x-notify-scopes": [ "teams:write" ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "name": { "type": "string", "minLength": 1, "maxLength": 60 }, "kind": { "type": "string", "const": "family" } }, "required": [ "name", "kind" ], "additionalProperties": false } } } }, "responses": { "201": { "description": "Success", "content": { "application/json": { "schema": { "type": "object", "properties": { "team": { "$ref": "#/components/schemas/Team" } }, "required": [ "team" ] } } } }, "400": { "description": "Invalid request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "401": { "description": "Missing, unknown, revoked, or expired token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "The token lacks a required scope or the account lacks permission", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "Not found for this account", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } ``` Resolve schema references against [OpenAPI](https://notify.belweave.ai/openapi.json). # A team and its members GET /api/agent/teams/{id}. Required scopes: teams:read. HTML: https://notify.belweave.ai/docs/api/getTeamsId Required scopes: `teams:read` ```http GET /api/agent/teams/{id} Authorization: Bearer ntf_your_token ``` ## Operation schema ```json { "summary": "A team and its members", "operationId": "getTeamsId", "security": [ { "bearer": [ "teams:read" ] } ], "x-notify-scopes": [ "teams:read" ], "parameters": [ { "name": "id", "in": "path", "schema": { "type": "string" }, "required": true } ], "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "type": "object", "properties": { "team": { "$ref": "#/components/schemas/Team" }, "members": { "type": "array", "items": { "$ref": "#/components/schemas/TeamMember" } } }, "required": [ "team", "members" ] } } } }, "400": { "description": "Invalid request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "401": { "description": "Missing, unknown, revoked, or expired token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "The token lacks a required scope or the account lacks permission", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "Not found for this account", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } ``` Resolve schema references against [OpenAPI](https://notify.belweave.ai/openapi.json). # Update a team (name: admin; auto-join and retention: owner) PATCH /api/agent/teams/{id}. Required scopes: teams:write. HTML: https://notify.belweave.ai/docs/api/patchTeamsId Required scopes: `teams:write` ```http PATCH /api/agent/teams/{id} Authorization: Bearer ntf_your_token ``` ## Operation schema ```json { "summary": "Update a team (name: admin; auto-join and retention: owner)", "operationId": "patchTeamsId", "security": [ { "bearer": [ "teams:write" ] } ], "x-notify-scopes": [ "teams:write" ], "parameters": [ { "name": "id", "in": "path", "schema": { "type": "string" }, "required": true } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "name": { "type": "string", "minLength": 1, "maxLength": 60 }, "autoJoinVerifiedDomain": { "type": "boolean" }, "retentionDays": { "anyOf": [ { "anyOf": [ { "type": "number", "const": 7 }, { "type": "number", "const": 30 }, { "type": "number", "const": 90 }, { "type": "number", "const": 365 } ] }, { "type": "null" } ] } }, "additionalProperties": false } } } }, "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "type": "object", "properties": { "team": { "$ref": "#/components/schemas/Team" } }, "required": [ "team" ] } } } }, "400": { "description": "Invalid request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "401": { "description": "Missing, unknown, revoked, or expired token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "The token lacks a required scope or the account lacks permission", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "Not found for this account", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } ``` Resolve schema references against [OpenAPI](https://notify.belweave.ai/openapi.json). # Delete a team (owner); its apps return to the members who added them DELETE /api/agent/teams/{id}. Required scopes: teams:write. HTML: https://notify.belweave.ai/docs/api/deleteTeamsId Required scopes: `teams:write` ```http DELETE /api/agent/teams/{id} Authorization: Bearer ntf_your_token ``` ## Operation schema ```json { "summary": "Delete a team (owner); its apps return to the members who added them", "operationId": "deleteTeamsId", "security": [ { "bearer": [ "teams:write" ] } ], "x-notify-scopes": [ "teams:write" ], "parameters": [ { "name": "id", "in": "path", "schema": { "type": "string" }, "required": true } ], "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "type": "object", "properties": { "ok": { "const": true } }, "required": [ "ok" ] } } } }, "400": { "description": "Invalid request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "401": { "description": "Missing, unknown, revoked, or expired token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "The token lacks a required scope or the account lacks permission", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "Not found for this account", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } ``` Resolve schema references against [OpenAPI](https://notify.belweave.ai/openapi.json). # Leave a team (the owner must transfer ownership first) POST /api/agent/teams/{id}/leave. Required scopes: teams:write. HTML: https://notify.belweave.ai/docs/api/postTeamsIdLeave Required scopes: `teams:write` ```http POST /api/agent/teams/{id}/leave Authorization: Bearer ntf_your_token ``` ## Operation schema ```json { "summary": "Leave a team (the owner must transfer ownership first)", "operationId": "postTeamsIdLeave", "security": [ { "bearer": [ "teams:write" ] } ], "x-notify-scopes": [ "teams:write" ], "parameters": [ { "name": "id", "in": "path", "schema": { "type": "string" }, "required": true } ], "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "type": "object", "properties": { "ok": { "const": true } }, "required": [ "ok" ] } } } }, "400": { "description": "Invalid request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "401": { "description": "Missing, unknown, revoked, or expired token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "The token lacks a required scope or the account lacks permission", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "Not found for this account", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } ``` Resolve schema references against [OpenAPI](https://notify.belweave.ai/openapi.json). # Change a member's role; `owner` transfers ownership (owner only) PATCH /api/agent/teams/{id}/members/{userId}. Required scopes: teams:write. HTML: https://notify.belweave.ai/docs/api/patchTeamsIdMembersUserId Required scopes: `teams:write` ```http PATCH /api/agent/teams/{id}/members/{userId} Authorization: Bearer ntf_your_token ``` ## Operation schema ```json { "summary": "Change a member's role; `owner` transfers ownership (owner only)", "operationId": "patchTeamsIdMembersUserId", "security": [ { "bearer": [ "teams:write" ] } ], "x-notify-scopes": [ "teams:write" ], "parameters": [ { "name": "id", "in": "path", "schema": { "type": "string" }, "required": true }, { "name": "userId", "in": "path", "schema": { "type": "string" }, "required": true } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "role": { "type": "string", "enum": [ "owner", "admin", "member" ] } }, "required": [ "role" ], "additionalProperties": false } } } }, "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "type": "object", "properties": { "member": { "$ref": "#/components/schemas/TeamMember" } }, "required": [ "member" ] } } } }, "400": { "description": "Invalid request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "401": { "description": "Missing, unknown, revoked, or expired token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "The token lacks a required scope or the account lacks permission", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "Not found for this account", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } ``` Resolve schema references against [OpenAPI](https://notify.belweave.ai/openapi.json). # Remove a member (admin or owner); the owner cannot be removed DELETE /api/agent/teams/{id}/members/{userId}. Required scopes: teams:write. HTML: https://notify.belweave.ai/docs/api/deleteTeamsIdMembersUserId Required scopes: `teams:write` ```http DELETE /api/agent/teams/{id}/members/{userId} Authorization: Bearer ntf_your_token ``` ## Operation schema ```json { "summary": "Remove a member (admin or owner); the owner cannot be removed", "operationId": "deleteTeamsIdMembersUserId", "security": [ { "bearer": [ "teams:write" ] } ], "x-notify-scopes": [ "teams:write" ], "parameters": [ { "name": "id", "in": "path", "schema": { "type": "string" }, "required": true }, { "name": "userId", "in": "path", "schema": { "type": "string" }, "required": true } ], "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "type": "object", "properties": { "ok": { "const": true } }, "required": [ "ok" ] } } } }, "400": { "description": "Invalid request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "401": { "description": "Missing, unknown, revoked, or expired token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "The token lacks a required scope or the account lacks permission", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "Not found for this account", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } ``` Resolve schema references against [OpenAPI](https://notify.belweave.ai/openapi.json). # List invites (admin or owner) GET /api/agent/teams/{id}/invites. Required scopes: teams:read. HTML: https://notify.belweave.ai/docs/api/getTeamsIdInvites Required scopes: `teams:read` ```http GET /api/agent/teams/{id}/invites Authorization: Bearer ntf_your_token ``` ## Operation schema ```json { "summary": "List invites (admin or owner)", "operationId": "getTeamsIdInvites", "security": [ { "bearer": [ "teams:read" ] } ], "x-notify-scopes": [ "teams:read" ], "parameters": [ { "name": "id", "in": "path", "schema": { "type": "string" }, "required": true } ], "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "type": "object", "properties": { "invites": { "type": "array", "items": { "$ref": "#/components/schemas/TeamInvite" } } }, "required": [ "invites" ] } } } }, "400": { "description": "Invalid request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "401": { "description": "Missing, unknown, revoked, or expired token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "The token lacks a required scope or the account lacks permission", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "Not found for this account", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } ``` Resolve schema references against [OpenAPI](https://notify.belweave.ai/openapi.json). # Create a 7-day join link (admin or owner) Family capacity and administrative member limits apply. Accepting an invite is human-only: there is no agent route for it. HTML: https://notify.belweave.ai/docs/api/postTeamsIdInvites Required scopes: `teams:write` ```http POST /api/agent/teams/{id}/invites Authorization: Bearer ntf_your_token ``` ## Operation schema ```json { "summary": "Create a 7-day join link (admin or owner)", "description": "Family capacity and administrative member limits apply. Accepting an invite is human-only: there is no agent route for it.", "operationId": "postTeamsIdInvites", "security": [ { "bearer": [ "teams:write" ] } ], "x-notify-scopes": [ "teams:write" ], "parameters": [ { "name": "id", "in": "path", "schema": { "type": "string" }, "required": true } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "email": { "type": "string", "maxLength": 254, "format": "email", "pattern": "^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$" }, "domain": { "type": "string", "maxLength": 253, "pattern": "^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\\.)+[a-z]{2,}$" }, "role": { "default": "member", "type": "string", "enum": [ "admin", "member" ] } }, "additionalProperties": false } } } }, "responses": { "201": { "description": "Success", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/TeamInviteCreated" } } } }, "400": { "description": "Invalid request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "401": { "description": "Missing, unknown, revoked, or expired token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "The token lacks a required scope or the account lacks permission", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "Not found for this account", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } ``` Resolve schema references against [OpenAPI](https://notify.belweave.ai/openapi.json). # Revoke an invite DELETE /api/agent/teams/{id}/invites/{inviteId}. Required scopes: teams:write. HTML: https://notify.belweave.ai/docs/api/deleteTeamsIdInvitesInviteId Required scopes: `teams:write` ```http DELETE /api/agent/teams/{id}/invites/{inviteId} Authorization: Bearer ntf_your_token ``` ## Operation schema ```json { "summary": "Revoke an invite", "operationId": "deleteTeamsIdInvitesInviteId", "security": [ { "bearer": [ "teams:write" ] } ], "x-notify-scopes": [ "teams:write" ], "parameters": [ { "name": "id", "in": "path", "schema": { "type": "string" }, "required": true }, { "name": "inviteId", "in": "path", "schema": { "type": "string" }, "required": true } ], "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "type": "object", "properties": { "ok": { "const": true } }, "required": [ "ok" ] } } } }, "400": { "description": "Invalid request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "401": { "description": "Missing, unknown, revoked, or expired token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "The token lacks a required scope or the account lacks permission", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "Not found for this account", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } ``` Resolve schema references against [OpenAPI](https://notify.belweave.ai/openapi.json). # The team's shared apps GET /api/agent/teams/{id}/apps. Required scopes: teams:read, apps:read. HTML: https://notify.belweave.ai/docs/api/getTeamsIdApps Required scopes: `teams:read`, `apps:read` ```http GET /api/agent/teams/{id}/apps Authorization: Bearer ntf_your_token ``` ## Operation schema ```json { "summary": "The team's shared apps", "operationId": "getTeamsIdApps", "security": [ { "bearer": [ "teams:read", "apps:read" ] } ], "x-notify-scopes": [ "teams:read", "apps:read" ], "parameters": [ { "name": "id", "in": "path", "schema": { "type": "string" }, "required": true } ], "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "type": "object", "properties": { "apps": { "type": "array", "items": { "$ref": "#/components/schemas/App" } } }, "required": [ "apps" ] } } } }, "400": { "description": "Invalid request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "401": { "description": "Missing, unknown, revoked, or expired token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "The token lacks a required scope or the account lacks permission", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "Not found for this account", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } ``` Resolve schema references against [OpenAPI](https://notify.belweave.ai/openapi.json). # The team's device groups GET /api/agent/teams/{id}/groups. Required scopes: teams:read. HTML: https://notify.belweave.ai/docs/api/getTeamsIdGroups Required scopes: `teams:read` ```http GET /api/agent/teams/{id}/groups Authorization: Bearer ntf_your_token ``` ## Operation schema ```json { "summary": "The team's device groups", "operationId": "getTeamsIdGroups", "security": [ { "bearer": [ "teams:read" ] } ], "x-notify-scopes": [ "teams:read" ], "parameters": [ { "name": "id", "in": "path", "schema": { "type": "string" }, "required": true } ], "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "type": "object", "properties": { "groups": { "type": "array", "items": { "$ref": "#/components/schemas/DeviceGroup" } } }, "required": [ "groups" ] } } } }, "400": { "description": "Invalid request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "401": { "description": "Missing, unknown, revoked, or expired token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "The token lacks a required scope or the account lacks permission", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "Not found for this account", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } ``` Resolve schema references against [OpenAPI](https://notify.belweave.ai/openapi.json). # Create a device group (admin or owner) POST /api/agent/teams/{id}/groups. Required scopes: teams:write. HTML: https://notify.belweave.ai/docs/api/postTeamsIdGroups Required scopes: `teams:write` ```http POST /api/agent/teams/{id}/groups Authorization: Bearer ntf_your_token ``` ## Operation schema ```json { "summary": "Create a device group (admin or owner)", "operationId": "postTeamsIdGroups", "security": [ { "bearer": [ "teams:write" ] } ], "x-notify-scopes": [ "teams:write" ], "parameters": [ { "name": "id", "in": "path", "schema": { "type": "string" }, "required": true } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "name": { "type": "string", "minLength": 1, "maxLength": 60 }, "memberIds": { "default": [], "maxItems": 200, "type": "array", "items": { "type": "string", "minLength": 1, "maxLength": 100 } }, "deviceIds": { "default": [], "maxItems": 200, "type": "array", "items": { "type": "string", "minLength": 1, "maxLength": 100 } } }, "required": [ "name" ], "additionalProperties": false } } } }, "responses": { "201": { "description": "Success", "content": { "application/json": { "schema": { "type": "object", "properties": { "group": { "$ref": "#/components/schemas/DeviceGroup" } }, "required": [ "group" ] } } } }, "400": { "description": "Invalid request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "401": { "description": "Missing, unknown, revoked, or expired token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "The token lacks a required scope or the account lacks permission", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "Not found for this account", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } ``` Resolve schema references against [OpenAPI](https://notify.belweave.ai/openapi.json). # A device group GET /api/agent/teams/{id}/groups/{groupId}. Required scopes: teams:read. HTML: https://notify.belweave.ai/docs/api/getTeamsIdGroupsGroupId Required scopes: `teams:read` ```http GET /api/agent/teams/{id}/groups/{groupId} Authorization: Bearer ntf_your_token ``` ## Operation schema ```json { "summary": "A device group", "operationId": "getTeamsIdGroupsGroupId", "security": [ { "bearer": [ "teams:read" ] } ], "x-notify-scopes": [ "teams:read" ], "parameters": [ { "name": "id", "in": "path", "schema": { "type": "string" }, "required": true }, { "name": "groupId", "in": "path", "schema": { "type": "string" }, "required": true } ], "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "type": "object", "properties": { "group": { "$ref": "#/components/schemas/DeviceGroup" } }, "required": [ "group" ] } } } }, "400": { "description": "Invalid request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "401": { "description": "Missing, unknown, revoked, or expired token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "The token lacks a required scope or the account lacks permission", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "Not found for this account", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } ``` Resolve schema references against [OpenAPI](https://notify.belweave.ai/openapi.json). # Update a device group (admin or owner) PATCH /api/agent/teams/{id}/groups/{groupId}. Required scopes: teams:write. HTML: https://notify.belweave.ai/docs/api/patchTeamsIdGroupsGroupId Required scopes: `teams:write` ```http PATCH /api/agent/teams/{id}/groups/{groupId} Authorization: Bearer ntf_your_token ``` ## Operation schema ```json { "summary": "Update a device group (admin or owner)", "operationId": "patchTeamsIdGroupsGroupId", "security": [ { "bearer": [ "teams:write" ] } ], "x-notify-scopes": [ "teams:write" ], "parameters": [ { "name": "id", "in": "path", "schema": { "type": "string" }, "required": true }, { "name": "groupId", "in": "path", "schema": { "type": "string" }, "required": true } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "name": { "type": "string", "minLength": 1, "maxLength": 60 }, "memberIds": { "maxItems": 200, "type": "array", "items": { "type": "string", "minLength": 1, "maxLength": 100 } }, "deviceIds": { "maxItems": 200, "type": "array", "items": { "type": "string", "minLength": 1, "maxLength": 100 } } }, "additionalProperties": false } } } }, "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "type": "object", "properties": { "group": { "$ref": "#/components/schemas/DeviceGroup" } }, "required": [ "group" ] } } } }, "400": { "description": "Invalid request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "401": { "description": "Missing, unknown, revoked, or expired token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "The token lacks a required scope or the account lacks permission", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "Not found for this account", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } ``` Resolve schema references against [OpenAPI](https://notify.belweave.ai/openapi.json). # Delete a device group (admin or owner) DELETE /api/agent/teams/{id}/groups/{groupId}. Required scopes: teams:write. HTML: https://notify.belweave.ai/docs/api/deleteTeamsIdGroupsGroupId Required scopes: `teams:write` ```http DELETE /api/agent/teams/{id}/groups/{groupId} Authorization: Bearer ntf_your_token ``` ## Operation schema ```json { "summary": "Delete a device group (admin or owner)", "operationId": "deleteTeamsIdGroupsGroupId", "security": [ { "bearer": [ "teams:write" ] } ], "x-notify-scopes": [ "teams:write" ], "parameters": [ { "name": "id", "in": "path", "schema": { "type": "string" }, "required": true }, { "name": "groupId", "in": "path", "schema": { "type": "string" }, "required": true } ], "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "type": "object", "properties": { "ok": { "const": true } }, "required": [ "ok" ] } } } }, "400": { "description": "Invalid request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "401": { "description": "Missing, unknown, revoked, or expired token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "The token lacks a required scope or the account lacks permission", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "Not found for this account", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } ``` Resolve schema references against [OpenAPI](https://notify.belweave.ai/openapi.json). # Devices belonging to team members (admin or owner) GET /api/agent/teams/{id}/devices. Required scopes: teams:read. HTML: https://notify.belweave.ai/docs/api/getTeamsIdDevices Required scopes: `teams:read` ```http GET /api/agent/teams/{id}/devices Authorization: Bearer ntf_your_token ``` ## Operation schema ```json { "summary": "Devices belonging to team members (admin or owner)", "operationId": "getTeamsIdDevices", "security": [ { "bearer": [ "teams:read" ] } ], "x-notify-scopes": [ "teams:read" ], "parameters": [ { "name": "id", "in": "path", "schema": { "type": "string" }, "required": true } ], "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "type": "object", "properties": { "devices": { "type": "array", "items": { "$ref": "#/components/schemas/DeviceGroupDevice" } } }, "required": [ "devices" ] } } } }, "400": { "description": "Invalid request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "401": { "description": "Missing, unknown, revoked, or expired token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "The token lacks a required scope or the account lacks permission", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "Not found for this account", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } ``` Resolve schema references against [OpenAPI](https://notify.belweave.ai/openapi.json). # Request a DNS TXT challenge for an email domain (owner) Stores a pending domain (not unique). Publish a TXT record at `_notify-verification.` with value `notify-verify=`, then POST `/teams/{id}/domain/verify`. Public mailbox domains are rejected. HTML: https://notify.belweave.ai/docs/api/postTeamsIdDomain Required scopes: `teams:write` ```http POST /api/agent/teams/{id}/domain Authorization: Bearer ntf_your_token ``` ## Operation schema ```json { "summary": "Request a DNS TXT challenge for an email domain (owner)", "description": "Stores a pending domain (not unique). Publish a TXT record at `_notify-verification.` with value `notify-verify=`, then POST `/teams/{id}/domain/verify`. Public mailbox domains are rejected.", "operationId": "postTeamsIdDomain", "security": [ { "bearer": [ "teams:write" ] } ], "x-notify-scopes": [ "teams:write" ], "parameters": [ { "name": "id", "in": "path", "schema": { "type": "string" }, "required": true } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "domain": { "type": "string", "maxLength": 253, "pattern": "^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\\.)+[a-z]{2,}$" } }, "required": [ "domain" ], "additionalProperties": false } } } }, "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "type": "object", "properties": { "team": { "$ref": "#/components/schemas/Team" }, "challenge": { "type": "object", "properties": { "domain": { "type": "string" }, "host": { "type": "string" }, "value": { "type": "string" } }, "required": [ "domain", "host", "value" ] } }, "required": [ "team", "challenge" ] } } } }, "400": { "description": "Invalid request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "401": { "description": "Missing, unknown, revoked, or expired token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "The token lacks a required scope or the account lacks permission", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "Not found for this account", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } ``` Resolve schema references against [OpenAPI](https://notify.belweave.ai/openapi.json). # Clear the pending or verified domain (owner) DELETE /api/agent/teams/{id}/domain. Required scopes: teams:write. HTML: https://notify.belweave.ai/docs/api/deleteTeamsIdDomain Required scopes: `teams:write` ```http DELETE /api/agent/teams/{id}/domain Authorization: Bearer ntf_your_token ``` ## Operation schema ```json { "summary": "Clear the pending or verified domain (owner)", "operationId": "deleteTeamsIdDomain", "security": [ { "bearer": [ "teams:write" ] } ], "x-notify-scopes": [ "teams:write" ], "parameters": [ { "name": "id", "in": "path", "schema": { "type": "string" }, "required": true } ], "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "type": "object", "properties": { "team": { "$ref": "#/components/schemas/Team" } }, "required": [ "team" ] } } } }, "400": { "description": "Invalid request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "401": { "description": "Missing, unknown, revoked, or expired token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "The token lacks a required scope or the account lacks permission", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "Not found for this account", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } ``` Resolve schema references against [OpenAPI](https://notify.belweave.ai/openapi.json). # Verify the pending domain's DNS TXT record (owner) Resolves the TXT challenge with a timeout. Resolver errors are cached at most a minute; TXT mismatches are not cached. Rate-limited per team. The verified domain is unique once this succeeds. HTML: https://notify.belweave.ai/docs/api/postTeamsIdDomainVerify Required scopes: `teams:write` ```http POST /api/agent/teams/{id}/domain/verify Authorization: Bearer ntf_your_token ``` ## Operation schema ```json { "summary": "Verify the pending domain's DNS TXT record (owner)", "description": "Resolves the TXT challenge with a timeout. Resolver errors are cached at most a minute; TXT mismatches are not cached. Rate-limited per team. The verified domain is unique once this succeeds.", "operationId": "postTeamsIdDomainVerify", "security": [ { "bearer": [ "teams:write" ] } ], "x-notify-scopes": [ "teams:write" ], "parameters": [ { "name": "id", "in": "path", "schema": { "type": "string" }, "required": true } ], "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "type": "object", "properties": { "team": { "$ref": "#/components/schemas/Team" } }, "required": [ "team" ] } } } }, "400": { "description": "Invalid request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "401": { "description": "Missing, unknown, revoked, or expired token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "The token lacks a required scope or the account lacks permission", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "Not found for this account", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } ``` Resolve schema references against [OpenAPI](https://notify.belweave.ai/openapi.json). # Audit log (admin or owner); members receive 403 `from` is inclusive. CSV (`format=csv`) paginates up to 50,000 rows and sets `X-Audit-Truncated: true` when more exist. Formula-like cells are prefixed with a quote. HTML: https://notify.belweave.ai/docs/api/getTeamsIdAudit Required scopes: `teams:read` ```http GET /api/agent/teams/{id}/audit Authorization: Bearer ntf_your_token ``` ## Operation schema ```json { "summary": "Audit log (admin or owner); members receive 403", "description": "`from` is inclusive. CSV (`format=csv`) paginates up to 50,000 rows and sets `X-Audit-Truncated: true` when more exist. Formula-like cells are prefixed with a quote.", "operationId": "getTeamsIdAudit", "security": [ { "bearer": [ "teams:read" ] } ], "x-notify-scopes": [ "teams:read" ], "parameters": [ { "name": "id", "in": "path", "schema": { "type": "string" }, "required": true }, { "name": "action", "in": "query", "schema": { "type": "string" }, "required": false }, { "name": "actor", "in": "query", "schema": { "type": "string" }, "required": false }, { "name": "from", "in": "query", "schema": { "type": "string", "format": "date-time" }, "required": false }, { "name": "to", "in": "query", "schema": { "type": "string", "format": "date-time" }, "required": false }, { "name": "cursor", "in": "query", "schema": { "type": "string" }, "required": false }, { "name": "limit", "in": "query", "schema": { "type": "integer", "minimum": 1, "maximum": 1000 }, "required": false }, { "name": "format", "in": "query", "schema": { "enum": [ "json", "csv" ] }, "required": false } ], "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/AuditLogPage" } } } }, "400": { "description": "Invalid request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "401": { "description": "Missing, unknown, revoked, or expired token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "The token lacks a required scope or the account lacks permission", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "Not found for this account", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } ``` Resolve schema references against [OpenAPI](https://notify.belweave.ai/openapi.json). # The team's on-call groups with current and upcoming shifts GET /api/agent/teams/{id}/oncall. Required scopes: oncall:read. HTML: https://notify.belweave.ai/docs/api/getTeamsIdOncall Required scopes: `oncall:read` ```http GET /api/agent/teams/{id}/oncall Authorization: Bearer ntf_your_token ``` ## Operation schema ```json { "summary": "The team's on-call groups with current and upcoming shifts", "operationId": "getTeamsIdOncall", "security": [ { "bearer": [ "oncall:read" ] } ], "x-notify-scopes": [ "oncall:read" ], "parameters": [ { "name": "id", "in": "path", "schema": { "type": "string" }, "required": true } ], "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "type": "object", "properties": { "groups": { "type": "array", "items": { "$ref": "#/components/schemas/OncallGroup" } } }, "required": [ "groups" ] } } } }, "400": { "description": "Invalid request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "401": { "description": "Missing, unknown, revoked, or expired token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "The token lacks a required scope or the account lacks permission", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "Not found for this account", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } ``` Resolve schema references against [OpenAPI](https://notify.belweave.ai/openapi.json). # Create an on-call group (admin or owner) POST /api/agent/teams/{id}/oncall. Required scopes: oncall:write. HTML: https://notify.belweave.ai/docs/api/postTeamsIdOncall Required scopes: `oncall:write` ```http POST /api/agent/teams/{id}/oncall Authorization: Bearer ntf_your_token ``` ## Operation schema ```json { "summary": "Create an on-call group (admin or owner)", "operationId": "postTeamsIdOncall", "security": [ { "bearer": [ "oncall:write" ] } ], "x-notify-scopes": [ "oncall:write" ], "parameters": [ { "name": "id", "in": "path", "schema": { "type": "string" }, "required": true } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "name": { "type": "string", "minLength": 1, "maxLength": 60 }, "rotation": { "type": "object", "properties": { "memberIds": { "minItems": 1, "maxItems": 100, "type": "array", "items": { "type": "string", "minLength": 1, "maxLength": 100 } }, "period": { "type": "string", "enum": [ "daily", "weekly" ] }, "handoffAt": { "type": "string", "pattern": "^([01]\\d|2[0-3]):[0-5]\\d$" }, "timezone": { "type": "string", "minLength": 1, "maxLength": 64 }, "startsAt": { "type": "string", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$" } }, "required": [ "memberIds", "period", "handoffAt", "timezone" ], "additionalProperties": false }, "escalation": { "default": [ { "afterMinutes": 5, "target": "next" }, { "afterMinutes": 10, "target": "group" } ], "maxItems": 5, "type": "array", "items": { "type": "object", "properties": { "afterMinutes": { "type": "integer", "minimum": 1, "maximum": 1440 }, "target": { "type": "string", "enum": [ "next", "group" ] } }, "required": [ "afterMinutes", "target" ], "additionalProperties": false } } }, "required": [ "name", "rotation" ], "additionalProperties": false } } } }, "responses": { "201": { "description": "Success", "content": { "application/json": { "schema": { "type": "object", "properties": { "group": { "$ref": "#/components/schemas/OncallGroup" } }, "required": [ "group" ] } } } }, "400": { "description": "Invalid request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "401": { "description": "Missing, unknown, revoked, or expired token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "The token lacks a required scope or the account lacks permission", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "Not found for this account", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } ``` Resolve schema references against [OpenAPI](https://notify.belweave.ai/openapi.json). # The team's pages, newest first GET /api/agent/teams/{id}/pages. Required scopes: oncall:read. HTML: https://notify.belweave.ai/docs/api/getTeamsIdPages Required scopes: `oncall:read` ```http GET /api/agent/teams/{id}/pages Authorization: Bearer ntf_your_token ``` ## Operation schema ```json { "summary": "The team's pages, newest first", "operationId": "getTeamsIdPages", "security": [ { "bearer": [ "oncall:read" ] } ], "x-notify-scopes": [ "oncall:read" ], "parameters": [ { "name": "id", "in": "path", "schema": { "type": "string" }, "required": true }, { "name": "status", "in": "query", "schema": { "enum": [ "open", "all" ] }, "required": false }, { "name": "cursor", "in": "query", "schema": { "type": "string" }, "required": false }, { "name": "limit", "in": "query", "schema": { "type": "integer", "minimum": 1, "maximum": 50 }, "required": false } ], "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "type": "object", "properties": { "pages": { "type": "array", "items": { "$ref": "#/components/schemas/OncallPage" } }, "nextCursor": { "type": [ "string", "null" ] } }, "required": [ "pages", "nextCursor" ] } } } }, "400": { "description": "Invalid request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "401": { "description": "Missing, unknown, revoked, or expired token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "The token lacks a required scope or the account lacks permission", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "Not found for this account", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } ``` Resolve schema references against [OpenAPI](https://notify.belweave.ai/openapi.json). # Your upcoming shifts and the open pages that notified you GET /api/agent/oncall/me. Required scopes: oncall:read. HTML: https://notify.belweave.ai/docs/api/getOncallMe Required scopes: `oncall:read` ```http GET /api/agent/oncall/me Authorization: Bearer ntf_your_token ``` ## Operation schema ```json { "summary": "Your upcoming shifts and the open pages that notified you", "operationId": "getOncallMe", "security": [ { "bearer": [ "oncall:read" ] } ], "x-notify-scopes": [ "oncall:read" ], "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "type": "object", "properties": { "shifts": { "type": "array", "items": { "allOf": [ { "$ref": "#/components/schemas/OncallShift" }, { "type": "object", "properties": { "groupId": { "type": "string" }, "groupName": { "type": "string" }, "teamId": { "type": "string" }, "teamName": { "type": "string" } }, "required": [ "groupId", "groupName", "teamId", "teamName" ] } ] } }, "pages": { "type": "array", "items": { "$ref": "#/components/schemas/OncallPage" } } }, "required": [ "shifts", "pages" ] } } } }, "400": { "description": "Invalid request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "401": { "description": "Missing, unknown, revoked, or expired token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "The token lacks a required scope or the account lacks permission", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "Not found for this account", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } ``` Resolve schema references against [OpenAPI](https://notify.belweave.ai/openapi.json). # An on-call group GET /api/agent/oncall/{groupId}. Required scopes: oncall:read. HTML: https://notify.belweave.ai/docs/api/getOncallGroupId Required scopes: `oncall:read` ```http GET /api/agent/oncall/{groupId} Authorization: Bearer ntf_your_token ``` ## Operation schema ```json { "summary": "An on-call group", "operationId": "getOncallGroupId", "security": [ { "bearer": [ "oncall:read" ] } ], "x-notify-scopes": [ "oncall:read" ], "parameters": [ { "name": "groupId", "in": "path", "schema": { "type": "string" }, "required": true } ], "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "type": "object", "properties": { "group": { "$ref": "#/components/schemas/OncallGroup" } }, "required": [ "group" ] } } } }, "400": { "description": "Invalid request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "401": { "description": "Missing, unknown, revoked, or expired token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "The token lacks a required scope or the account lacks permission", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "Not found for this account", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } ``` Resolve schema references against [OpenAPI](https://notify.belweave.ai/openapi.json). # Edit a group's name, rotation, or escalation (admin or owner) PATCH /api/agent/oncall/{groupId}. Required scopes: oncall:write. HTML: https://notify.belweave.ai/docs/api/patchOncallGroupId Required scopes: `oncall:write` ```http PATCH /api/agent/oncall/{groupId} Authorization: Bearer ntf_your_token ``` ## Operation schema ```json { "summary": "Edit a group's name, rotation, or escalation (admin or owner)", "operationId": "patchOncallGroupId", "security": [ { "bearer": [ "oncall:write" ] } ], "x-notify-scopes": [ "oncall:write" ], "parameters": [ { "name": "groupId", "in": "path", "schema": { "type": "string" }, "required": true } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "name": { "type": "string", "minLength": 1, "maxLength": 60 }, "rotation": { "type": "object", "properties": { "memberIds": { "minItems": 1, "maxItems": 100, "type": "array", "items": { "type": "string", "minLength": 1, "maxLength": 100 } }, "period": { "type": "string", "enum": [ "daily", "weekly" ] }, "handoffAt": { "type": "string", "pattern": "^([01]\\d|2[0-3]):[0-5]\\d$" }, "timezone": { "type": "string", "minLength": 1, "maxLength": 64 }, "startsAt": { "type": "string", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$" } }, "required": [ "memberIds", "period", "handoffAt", "timezone" ], "additionalProperties": false }, "escalation": { "maxItems": 5, "type": "array", "items": { "type": "object", "properties": { "afterMinutes": { "type": "integer", "minimum": 1, "maximum": 1440 }, "target": { "type": "string", "enum": [ "next", "group" ] } }, "required": [ "afterMinutes", "target" ], "additionalProperties": false } } }, "additionalProperties": false } } } }, "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "type": "object", "properties": { "group": { "$ref": "#/components/schemas/OncallGroup" } }, "required": [ "group" ] } } } }, "400": { "description": "Invalid request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "401": { "description": "Missing, unknown, revoked, or expired token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "The token lacks a required scope or the account lacks permission", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "Not found for this account", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } ``` Resolve schema references against [OpenAPI](https://notify.belweave.ai/openapi.json). # Delete an on-call group (admin or owner) DELETE /api/agent/oncall/{groupId}. Required scopes: oncall:write. HTML: https://notify.belweave.ai/docs/api/deleteOncallGroupId Required scopes: `oncall:write` ```http DELETE /api/agent/oncall/{groupId} Authorization: Bearer ntf_your_token ``` ## Operation schema ```json { "summary": "Delete an on-call group (admin or owner)", "operationId": "deleteOncallGroupId", "security": [ { "bearer": [ "oncall:write" ] } ], "x-notify-scopes": [ "oncall:write" ], "parameters": [ { "name": "groupId", "in": "path", "schema": { "type": "string" }, "required": true } ], "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "type": "object", "properties": { "ok": { "const": true } }, "required": [ "ok" ] } } } }, "400": { "description": "Invalid request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "401": { "description": "Missing, unknown, revoked, or expired token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "The token lacks a required scope or the account lacks permission", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "Not found for this account", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } ``` Resolve schema references against [OpenAPI](https://notify.belweave.ai/openapi.json). # Put someone on call for a window (admins: anyone; members: themselves) POST /api/agent/oncall/{groupId}/overrides. Required scopes: oncall:write. HTML: https://notify.belweave.ai/docs/api/postOncallGroupIdOverrides Required scopes: `oncall:write` ```http POST /api/agent/oncall/{groupId}/overrides Authorization: Bearer ntf_your_token ``` ## Operation schema ```json { "summary": "Put someone on call for a window (admins: anyone; members: themselves)", "operationId": "postOncallGroupIdOverrides", "security": [ { "bearer": [ "oncall:write" ] } ], "x-notify-scopes": [ "oncall:write" ], "parameters": [ { "name": "groupId", "in": "path", "schema": { "type": "string" }, "required": true } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "userId": { "type": "string", "minLength": 1, "maxLength": 100 }, "startsAt": { "type": "string", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$" }, "endsAt": { "type": "string", "format": "date-time", "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$" } }, "required": [ "userId", "startsAt", "endsAt" ], "additionalProperties": false } } } }, "responses": { "201": { "description": "Success", "content": { "application/json": { "schema": { "type": "object", "properties": { "group": { "$ref": "#/components/schemas/OncallGroup" } }, "required": [ "group" ] } } } }, "400": { "description": "Invalid request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "401": { "description": "Missing, unknown, revoked, or expired token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "The token lacks a required scope or the account lacks permission", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "Not found for this account", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } ``` Resolve schema references against [OpenAPI](https://notify.belweave.ai/openapi.json). # Remove an override DELETE /api/agent/oncall/{groupId}/overrides/{overrideId}. Required scopes: oncall:write. HTML: https://notify.belweave.ai/docs/api/deleteOncallGroupIdOverridesOverrideId Required scopes: `oncall:write` ```http DELETE /api/agent/oncall/{groupId}/overrides/{overrideId} Authorization: Bearer ntf_your_token ``` ## Operation schema ```json { "summary": "Remove an override", "operationId": "deleteOncallGroupIdOverridesOverrideId", "security": [ { "bearer": [ "oncall:write" ] } ], "x-notify-scopes": [ "oncall:write" ], "parameters": [ { "name": "groupId", "in": "path", "schema": { "type": "string" }, "required": true }, { "name": "overrideId", "in": "path", "schema": { "type": "string" }, "required": true } ], "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "type": "object", "properties": { "group": { "$ref": "#/components/schemas/OncallGroup" } }, "required": [ "group" ] } } } }, "400": { "description": "Invalid request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "401": { "description": "Missing, unknown, revoked, or expired token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "The token lacks a required scope or the account lacks permission", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "Not found for this account", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } ``` Resolve schema references against [OpenAPI](https://notify.belweave.ai/openapi.json). # Page the group: whoever is on call first, then escalation Returns 200 with `deduplicated: true` when an open page with the same `dedupKey` absorbed it. Pages have no monthly notification cap. HTML: https://notify.belweave.ai/docs/api/postOncallGroupIdPages Required scopes: `oncall:write` ```http POST /api/agent/oncall/{groupId}/pages Authorization: Bearer ntf_your_token ``` ## Operation schema ```json { "summary": "Page the group: whoever is on call first, then escalation", "description": "Returns 200 with `deduplicated: true` when an open page with the same `dedupKey` absorbed it. Pages have no monthly notification cap.", "operationId": "postOncallGroupIdPages", "security": [ { "bearer": [ "oncall:write" ] } ], "x-notify-scopes": [ "oncall:write" ], "parameters": [ { "name": "groupId", "in": "path", "schema": { "type": "string" }, "required": true } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "title": { "type": "string", "minLength": 1, "maxLength": 120 }, "body": { "type": "string", "maxLength": 2000 }, "url": { "type": "string", "maxLength": 2048, "format": "uri" }, "appId": { "type": "string", "pattern": "^app_[A-Za-z0-9_-]{8,64}$" }, "dedupKey": { "type": "string", "minLength": 1, "maxLength": 200 }, "group": { "type": "string", "pattern": "^dgrp_[A-Za-z0-9_-]{8,64}$" } }, "required": [ "title" ], "additionalProperties": false } } } }, "responses": { "201": { "description": "Success", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/OncallPageCreated" } } } }, "400": { "description": "Invalid request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "401": { "description": "Missing, unknown, revoked, or expired token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "The token lacks a required scope or the account lacks permission", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "Not found for this account", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } ``` Resolve schema references against [OpenAPI](https://notify.belweave.ai/openapi.json). # A page There is deliberately no agent route to acknowledge or escalate: an acknowledgement tells the team a person is on it, so only a human can give it (on the phone or in a signed-in session). HTML: https://notify.belweave.ai/docs/api/getPagesId Required scopes: `oncall:read` ```http GET /api/agent/pages/{id} Authorization: Bearer ntf_your_token ``` ## Operation schema ```json { "summary": "A page", "description": "There is deliberately no agent route to acknowledge or escalate: an acknowledgement tells the team a person is on it, so only a human can give it (on the phone or in a signed-in session).", "operationId": "getPagesId", "security": [ { "bearer": [ "oncall:read" ] } ], "x-notify-scopes": [ "oncall:read" ], "parameters": [ { "name": "id", "in": "path", "schema": { "type": "string" }, "required": true } ], "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "type": "object", "properties": { "page": { "$ref": "#/components/schemas/OncallPage" } }, "required": [ "page" ] } } } }, "400": { "description": "Invalid request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "401": { "description": "Missing, unknown, revoked, or expired token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "The token lacks a required scope or the account lacks permission", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "Not found for this account", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } ``` Resolve schema references against [OpenAPI](https://notify.belweave.ai/openapi.json). # List tunnel connectors GET /api/agent/connectors. Required scopes: connectors:read. HTML: https://notify.belweave.ai/docs/api/getConnectors Required scopes: `connectors:read` ```http GET /api/agent/connectors Authorization: Bearer ntf_your_token ``` ## Operation schema ```json { "summary": "List tunnel connectors", "operationId": "getConnectors", "security": [ { "bearer": [ "connectors:read" ] } ], "x-notify-scopes": [ "connectors:read" ], "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "type": "object", "properties": { "connectors": { "type": "array", "items": { "$ref": "#/components/schemas/Connector" } } }, "required": [ "connectors" ] } } } }, "400": { "description": "Invalid request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "401": { "description": "Missing, unknown, revoked, or expired token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "The token lacks a required scope or the account lacks permission", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "Not found for this account", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } ``` Resolve schema references against [OpenAPI](https://notify.belweave.ai/openapi.json). # Register a tunnel connector; the callback HMAC secret is returned once POST /api/agent/connectors. Required scopes: connectors:write. HTML: https://notify.belweave.ai/docs/api/postConnectors Required scopes: `connectors:write` ```http POST /api/agent/connectors Authorization: Bearer ntf_your_token ``` ## Operation schema ```json { "summary": "Register a tunnel connector; the callback HMAC secret is returned once", "operationId": "postConnectors", "security": [ { "bearer": [ "connectors:write" ] } ], "x-notify-scopes": [ "connectors:write" ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "name": { "type": "string", "minLength": 1, "maxLength": 80 }, "tunnelId": { "type": "string", "minLength": 1, "maxLength": 64, "pattern": "^[a-z0-9]+$" }, "hostname": { "type": "string", "minLength": 1, "maxLength": 253 }, "routes": { "maxItems": 32, "type": "array", "items": { "type": "object", "properties": { "name": { "type": "string", "pattern": "^(@|[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)$" }, "target": { "type": "string", "minLength": 1, "maxLength": 200, "pattern": "^[A-Za-z0-9._[\\]:-]+:\\d{1,5}$" }, "url": { "type": "string", "maxLength": 2048, "format": "uri" }, "appId": { "type": "string", "pattern": "^app_[A-Za-z0-9_-]{8,64}$" }, "enabled": { "type": "boolean" } }, "required": [ "name", "target", "url" ] } }, "teamId": { "type": "string", "pattern": "^team_[A-Za-z0-9_-]{8,64}$", "description": "Team or free Family ID (team_…) returned by teams_create or teams_list" }, "oncallGroupId": { "type": "string", "pattern": "^ocg_[A-Za-z0-9_-]{8,64}$" } }, "required": [ "name", "tunnelId", "hostname" ] } } } }, "responses": { "201": { "description": "Success", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ConnectorCreated" } } } }, "400": { "description": "Invalid request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "401": { "description": "Missing, unknown, revoked, or expired token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "The token lacks a required scope or the account lacks permission", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "Not found for this account", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } ``` Resolve schema references against [OpenAPI](https://notify.belweave.ai/openapi.json). # A tunnel connector GET /api/agent/connectors/{id}. Required scopes: connectors:read. HTML: https://notify.belweave.ai/docs/api/getConnectorsId Required scopes: `connectors:read` ```http GET /api/agent/connectors/{id} Authorization: Bearer ntf_your_token ``` ## Operation schema ```json { "summary": "A tunnel connector", "operationId": "getConnectorsId", "security": [ { "bearer": [ "connectors:read" ] } ], "x-notify-scopes": [ "connectors:read" ], "parameters": [ { "name": "id", "in": "path", "schema": { "type": "string" }, "required": true } ], "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "type": "object", "properties": { "connector": { "$ref": "#/components/schemas/Connector" } }, "required": [ "connector" ] } } } }, "400": { "description": "Invalid request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "401": { "description": "Missing, unknown, revoked, or expired token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "The token lacks a required scope or the account lacks permission", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "Not found for this account", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } ``` Resolve schema references against [OpenAPI](https://notify.belweave.ai/openapi.json). # Update a connector's name, routes, or on-call group PATCH /api/agent/connectors/{id}. Required scopes: connectors:write. HTML: https://notify.belweave.ai/docs/api/patchConnectorsId Required scopes: `connectors:write` ```http PATCH /api/agent/connectors/{id} Authorization: Bearer ntf_your_token ``` ## Operation schema ```json { "summary": "Update a connector's name, routes, or on-call group", "operationId": "patchConnectorsId", "security": [ { "bearer": [ "connectors:write" ] } ], "x-notify-scopes": [ "connectors:write" ], "parameters": [ { "name": "id", "in": "path", "schema": { "type": "string" }, "required": true } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "enabled": { "type": "boolean" }, "name": { "type": "string", "minLength": 1, "maxLength": 80 }, "tunnelId": { "type": "string", "minLength": 1, "maxLength": 64, "pattern": "^[a-z0-9]+$" }, "hostname": { "type": "string", "minLength": 1, "maxLength": 253 }, "routes": { "maxItems": 32, "type": "array", "items": { "type": "object", "properties": { "name": { "type": "string", "pattern": "^(@|[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)$" }, "target": { "type": "string", "minLength": 1, "maxLength": 200, "pattern": "^[A-Za-z0-9._[\\]:-]+:\\d{1,5}$" }, "url": { "type": "string", "maxLength": 2048, "format": "uri" }, "appId": { "type": "string", "pattern": "^app_[A-Za-z0-9_-]{8,64}$" }, "enabled": { "type": "boolean" } }, "required": [ "name", "target", "url" ] } }, "oncallGroupId": { "anyOf": [ { "type": "string", "pattern": "^ocg_[A-Za-z0-9_-]{8,64}$" }, { "type": "null" } ] }, "status": { "type": "string", "enum": [ "online", "offline", "unknown" ] } } } } } }, "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "type": "object", "properties": { "connector": { "$ref": "#/components/schemas/Connector" } }, "required": [ "connector" ] } } } }, "400": { "description": "Invalid request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "401": { "description": "Missing, unknown, revoked, or expired token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "The token lacks a required scope or the account lacks permission", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "Not found for this account", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } ``` Resolve schema references against [OpenAPI](https://notify.belweave.ai/openapi.json). # Remove a tunnel connector DELETE /api/agent/connectors/{id}. Required scopes: connectors:write. HTML: https://notify.belweave.ai/docs/api/deleteConnectorsId Required scopes: `connectors:write` ```http DELETE /api/agent/connectors/{id} Authorization: Bearer ntf_your_token ``` ## Operation schema ```json { "summary": "Remove a tunnel connector", "operationId": "deleteConnectorsId", "security": [ { "bearer": [ "connectors:write" ] } ], "x-notify-scopes": [ "connectors:write" ], "parameters": [ { "name": "id", "in": "path", "schema": { "type": "string" }, "required": true } ], "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "type": "object", "properties": { "ok": { "const": true } }, "required": [ "ok" ] } } } }, "400": { "description": "Invalid request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "401": { "description": "Missing, unknown, revoked, or expired token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "The token lacks a required scope or the account lacks permission", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "Not found for this account", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } ``` Resolve schema references against [OpenAPI](https://notify.belweave.ai/openapi.json). # Mark a connector online and refresh its last-seen time POST /api/agent/connectors/{id}/heartbeat. Required scopes: connectors:write. HTML: https://notify.belweave.ai/docs/api/postConnectorsIdHeartbeat Required scopes: `connectors:write` ```http POST /api/agent/connectors/{id}/heartbeat Authorization: Bearer ntf_your_token ``` ## Operation schema ```json { "summary": "Mark a connector online and refresh its last-seen time", "operationId": "postConnectorsIdHeartbeat", "security": [ { "bearer": [ "connectors:write" ] } ], "x-notify-scopes": [ "connectors:write" ], "parameters": [ { "name": "id", "in": "path", "schema": { "type": "string" }, "required": true } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "tunnelId": { "type": "string", "minLength": 1, "maxLength": 64, "pattern": "^[a-z0-9]+$" }, "hostname": { "type": "string", "minLength": 1, "maxLength": 253 }, "routes": { "maxItems": 32, "type": "array", "items": { "type": "object", "properties": { "name": { "type": "string", "pattern": "^(@|[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)$" }, "target": { "type": "string", "minLength": 1, "maxLength": 200, "pattern": "^[A-Za-z0-9._[\\]:-]+:\\d{1,5}$" }, "url": { "type": "string", "maxLength": 2048, "format": "uri" }, "appId": { "type": "string", "pattern": "^app_[A-Za-z0-9_-]{8,64}$" }, "enabled": { "type": "boolean" } }, "required": [ "name", "target", "url" ] } }, "status": { "type": "string", "enum": [ "online", "offline", "unknown" ] }, "graceful": { "type": "boolean" } } } } } }, "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "type": "object", "properties": { "connector": { "$ref": "#/components/schemas/Connector" } }, "required": [ "connector" ] } } } }, "400": { "description": "Invalid request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "401": { "description": "Missing, unknown, revoked, or expired token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "The token lacks a required scope or the account lacks permission", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "Not found for this account", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } ``` Resolve schema references against [OpenAPI](https://notify.belweave.ai/openapi.json). # Connector gateway: consume a one-time Notify pass or revalidate its bound session on every request POST /api/agent/connectors/{id}/access. Required scopes: connectors:write. HTML: https://notify.belweave.ai/docs/api/postConnectorsIdAccess Required scopes: `connectors:write` ```http POST /api/agent/connectors/{id}/access Authorization: Bearer ntf_your_token ``` ## Operation schema ```json { "summary": "Connector gateway: consume a one-time Notify pass or revalidate its bound session on every request", "operationId": "postConnectorsIdAccess", "security": [ { "bearer": [ "connectors:write" ] } ], "x-notify-scopes": [ "connectors:write" ], "parameters": [ { "name": "id", "in": "path", "schema": { "type": "string" }, "required": true } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "routeName": { "type": "string" }, "pass": { "type": "string", "minLength": 1, "maxLength": 16384 }, "consume": { "type": "boolean" } }, "required": [ "routeName", "pass", "consume" ], "additionalProperties": false } } } }, "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "type": "object", "properties": { "identity": { "type": "object", "properties": { "subject": { "type": "string" }, "appId": { "type": "string" }, "teamId": { "type": [ "string", "null" ] }, "teamRole": { "type": [ "string", "null" ] }, "expiresAt": { "type": "integer" } }, "required": [ "subject", "appId", "teamId", "teamRole", "expiresAt" ] } }, "required": [ "identity" ] } } } }, "400": { "description": "Invalid request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "401": { "description": "Missing, unknown, revoked, or expired token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "The token lacks a required scope or the account lacks permission", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "Not found for this account", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } ``` Resolve schema references against [OpenAPI](https://notify.belweave.ai/openapi.json). # Connector gateway: authorize one signed, recorded callback delivery and return its saved destination path POST /api/agent/connectors/{id}/callback-access. Required scopes: connectors:write. HTML: https://notify.belweave.ai/docs/api/postConnectorsIdCallbackAccess Required scopes: `connectors:write` ```http POST /api/agent/connectors/{id}/callback-access Authorization: Bearer ntf_your_token ``` ## Operation schema ```json { "summary": "Connector gateway: authorize one signed, recorded callback delivery and return its saved destination path", "operationId": "postConnectorsIdCallbackAccess", "security": [ { "bearer": [ "connectors:write" ] } ], "x-notify-scopes": [ "connectors:write" ], "parameters": [ { "name": "id", "in": "path", "schema": { "type": "string" }, "required": true } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "routeName": { "type": "string" }, "body": { "type": "string", "minLength": 1, "maxLength": 65536 }, "timestamp": { "type": "string" }, "signature": { "type": "string" } }, "required": [ "routeName", "body", "timestamp", "signature" ], "additionalProperties": false } } } }, "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "type": "object", "properties": { "path": { "type": "string" } }, "required": [ "path" ] } } } }, "400": { "description": "Invalid request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "401": { "description": "Missing, unknown, revoked, or expired token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "The token lacks a required scope or the account lacks permission", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "Not found for this account", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } ``` Resolve schema references against [OpenAPI](https://notify.belweave.ai/openapi.json). # Rotate a connector callback HMAC secret; the new secret is returned once POST /api/agent/connectors/{id}/rotate-secret. Required scopes: connectors:write. HTML: https://notify.belweave.ai/docs/api/postConnectorsIdRotateSecret Required scopes: `connectors:write` ```http POST /api/agent/connectors/{id}/rotate-secret Authorization: Bearer ntf_your_token ``` ## Operation schema ```json { "summary": "Rotate a connector callback HMAC secret; the new secret is returned once", "operationId": "postConnectorsIdRotateSecret", "security": [ { "bearer": [ "connectors:write" ] } ], "x-notify-scopes": [ "connectors:write" ], "parameters": [ { "name": "id", "in": "path", "schema": { "type": "string" }, "required": true } ], "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ConnectorCreated" } } } }, "400": { "description": "Invalid request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "401": { "description": "Missing, unknown, revoked, or expired token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "The token lacks a required scope or the account lacks permission", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "Not found for this account", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } ``` Resolve schema references against [OpenAPI](https://notify.belweave.ai/openapi.json). # Resolve a page; anyone still being paged is cleared POST /api/agent/pages/{id}/resolve. Required scopes: oncall:write. HTML: https://notify.belweave.ai/docs/api/postPagesIdResolve Required scopes: `oncall:write` ```http POST /api/agent/pages/{id}/resolve Authorization: Bearer ntf_your_token ``` ## Operation schema ```json { "summary": "Resolve a page; anyone still being paged is cleared", "operationId": "postPagesIdResolve", "security": [ { "bearer": [ "oncall:write" ] } ], "x-notify-scopes": [ "oncall:write" ], "parameters": [ { "name": "id", "in": "path", "schema": { "type": "string" }, "required": true } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "note": { "type": "string", "maxLength": 500 } }, "additionalProperties": false } } } }, "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "type": "object", "properties": { "page": { "$ref": "#/components/schemas/OncallPage" } }, "required": [ "page" ] } } } }, "400": { "description": "Invalid request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "401": { "description": "Missing, unknown, revoked, or expired token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "The token lacks a required scope or the account lacks permission", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "Not found for this account", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } ``` Resolve schema references against [OpenAPI](https://notify.belweave.ai/openapi.json). # Inbox projects with unread counts GET /api/agent/inbox/projects. Required scopes: inbox:read. HTML: https://notify.belweave.ai/docs/api/getInboxProjects Required scopes: `inbox:read` ```http GET /api/agent/inbox/projects Authorization: Bearer ntf_your_token ``` ## Operation schema ```json { "summary": "Inbox projects with unread counts", "operationId": "getInboxProjects", "security": [ { "bearer": [ "inbox:read" ] } ], "x-notify-scopes": [ "inbox:read" ], "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/InboxProjects" } } } }, "400": { "description": "Invalid request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "401": { "description": "Missing, unknown, revoked, or expired token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "The token lacks a required scope or the account lacks permission", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "Not found for this account", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } ``` Resolve schema references against [OpenAPI](https://notify.belweave.ai/openapi.json). # Inbox notifications, newest first, with cursor pagination GET /api/agent/inbox/notifications. Required scopes: inbox:read. HTML: https://notify.belweave.ai/docs/api/getInboxNotifications Required scopes: `inbox:read` ```http GET /api/agent/inbox/notifications Authorization: Bearer ntf_your_token ``` ## Operation schema ```json { "summary": "Inbox notifications, newest first, with cursor pagination", "operationId": "getInboxNotifications", "security": [ { "bearer": [ "inbox:read" ] } ], "x-notify-scopes": [ "inbox:read" ], "parameters": [ { "name": "limit", "in": "query", "schema": { "type": "integer", "minimum": 1, "maximum": 50 }, "required": false }, { "name": "project", "in": "query", "schema": { "type": "string", "description": "Project ID, or `unfiled`." }, "required": false }, { "name": "unread", "in": "query", "schema": { "enum": [ "1", "true" ] }, "required": false }, { "name": "cursor", "in": "query", "schema": { "type": "string" }, "required": false } ], "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/InboxNotificationPage" } } } }, "400": { "description": "Invalid request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "401": { "description": "Missing, unknown, revoked, or expired token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "The token lacks a required scope or the account lacks permission", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "Not found for this account", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } ``` Resolve schema references against [OpenAPI](https://notify.belweave.ai/openapi.json). # One inbox notification with its full body GET /api/agent/inbox/notifications/{id}. Required scopes: inbox:read. HTML: https://notify.belweave.ai/docs/api/getInboxNotificationsId Required scopes: `inbox:read` ```http GET /api/agent/inbox/notifications/{id} Authorization: Bearer ntf_your_token ``` ## Operation schema ```json { "summary": "One inbox notification with its full body", "operationId": "getInboxNotificationsId", "security": [ { "bearer": [ "inbox:read" ] } ], "x-notify-scopes": [ "inbox:read" ], "parameters": [ { "name": "id", "in": "path", "schema": { "type": "string" }, "required": true } ], "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "type": "object", "properties": { "notification": { "$ref": "#/components/schemas/InboxNotificationDetail" } }, "required": [ "notification" ] } } } }, "400": { "description": "Invalid request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "401": { "description": "Missing, unknown, revoked, or expired token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "The token lacks a required scope or the account lacks permission", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "Not found for this account", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } ``` Resolve schema references against [OpenAPI](https://notify.belweave.ai/openapi.json). # Mark everything up to a list response's readThroughToken read POST /api/agent/inbox/notifications/read-all. Required scopes: inbox:write. HTML: https://notify.belweave.ai/docs/api/postInboxNotificationsReadAll Required scopes: `inbox:write` ```http POST /api/agent/inbox/notifications/read-all Authorization: Bearer ntf_your_token ``` ## Operation schema ```json { "summary": "Mark everything up to a list response's readThroughToken read", "operationId": "postInboxNotificationsReadAll", "security": [ { "bearer": [ "inbox:write" ] } ], "x-notify-scopes": [ "inbox:write" ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "readThrough": { "type": "string", "minLength": 1, "maxLength": 200 }, "project": { "type": "string", "minLength": 1, "maxLength": 100 } }, "required": [ "readThrough" ] } } } }, "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "type": "object", "properties": { "ok": { "const": true }, "updated": { "type": "integer" } }, "required": [ "ok", "updated" ] } } } }, "400": { "description": "Invalid request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "401": { "description": "Missing, unknown, revoked, or expired token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "The token lacks a required scope or the account lacks permission", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "Not found for this account", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } ``` Resolve schema references against [OpenAPI](https://notify.belweave.ai/openapi.json). # Mark one notification read POST /api/agent/inbox/notifications/{id}/read. Required scopes: inbox:write. HTML: https://notify.belweave.ai/docs/api/postInboxNotificationsIdRead Required scopes: `inbox:write` ```http POST /api/agent/inbox/notifications/{id}/read Authorization: Bearer ntf_your_token ``` ## Operation schema ```json { "summary": "Mark one notification read", "operationId": "postInboxNotificationsIdRead", "security": [ { "bearer": [ "inbox:write" ] } ], "x-notify-scopes": [ "inbox:write" ], "parameters": [ { "name": "id", "in": "path", "schema": { "type": "string" }, "required": true } ], "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ReadMark" } } } }, "400": { "description": "Invalid request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "401": { "description": "Missing, unknown, revoked, or expired token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "The token lacks a required scope or the account lacks permission", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "Not found for this account", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } ``` Resolve schema references against [OpenAPI](https://notify.belweave.ai/openapi.json). # Mark one notification unread POST /api/agent/inbox/notifications/{id}/unread. Required scopes: inbox:write. HTML: https://notify.belweave.ai/docs/api/postInboxNotificationsIdUnread Required scopes: `inbox:write` ```http POST /api/agent/inbox/notifications/{id}/unread Authorization: Bearer ntf_your_token ``` ## Operation schema ```json { "summary": "Mark one notification unread", "operationId": "postInboxNotificationsIdUnread", "security": [ { "bearer": [ "inbox:write" ] } ], "x-notify-scopes": [ "inbox:write" ], "parameters": [ { "name": "id", "in": "path", "schema": { "type": "string" }, "required": true } ], "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ReadMark" } } } }, "400": { "description": "Invalid request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "401": { "description": "Missing, unknown, revoked, or expired token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "The token lacks a required scope or the account lacks permission", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "Not found for this account", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } ``` Resolve schema references against [OpenAPI](https://notify.belweave.ai/openapi.json). # Plan, limits, and remaining usage Read-only Free entitlements, unlimited notifications and devices, and per-minute rate limits. HTML: https://notify.belweave.ai/docs/api/getBilling Required scopes: `billing:read` ```http GET /api/agent/billing Authorization: Bearer ntf_your_token ``` ## Operation schema ```json { "summary": "Plan, limits, and remaining usage", "description": "Read-only Free entitlements, unlimited notifications and devices, and per-minute rate limits.", "operationId": "getBilling", "security": [ { "bearer": [ "billing:read" ] } ], "x-notify-scopes": [ "billing:read" ], "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Billing" } } } }, "400": { "description": "Invalid request", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "401": { "description": "Missing, unknown, revoked, or expired token", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "403": { "description": "The token lacks a required scope or the account lacks permission", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }, "404": { "description": "Not found for this account", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } } } } ``` Resolve schema references against [OpenAPI](https://notify.belweave.ai/openapi.json). ## Shared OpenAPI schemas ```json { "securitySchemes": { "bearer": { "type": "http", "scheme": "bearer", "description": "A Notify agent token from `notify auth login` or the dashboard." } }, "schemas": { "Error": { "type": "object", "properties": { "error": { "type": "string" }, "code": { "type": "string" }, "issues": {}, "required": { "type": "array", "items": { "type": "string" } } }, "required": [ "error" ] }, "Ok": { "type": "object", "properties": { "ok": { "const": true } }, "required": [ "ok" ] }, "TokenStatus": { "type": "object", "properties": { "authenticated": { "const": true }, "token": { "type": "object", "properties": { "id": { "type": "string" }, "name": { "type": "string" }, "prefix": { "type": "string" }, "scopes": { "type": "array", "items": { "$ref": "#/components/schemas/Scope" } }, "createdAt": { "type": "string", "format": "date-time" }, "lastUsedAt": { "type": [ "string", "null" ], "format": "date-time" }, "expiresAt": { "type": [ "string", "null" ], "format": "date-time" } }, "required": [ "id", "name", "prefix", "scopes", "createdAt", "lastUsedAt", "expiresAt" ] } }, "required": [ "authenticated", "token" ] }, "Scope": { "type": "string", "enum": [ "notifications:send", "interactions:create", "interactions:read", "activities:read", "activities:write", "services:read", "services:write", "devices:read", "events:read", "apps:read", "apps:write", "devices:write", "inbox:read", "inbox:write", "billing:read", "tokens:manage", "teams:read", "teams:write", "oncall:read", "oncall:write", "connectors:read", "connectors:write" ] }, "ApiToken": { "type": "object", "properties": { "id": { "type": "string" }, "name": { "type": "string" }, "prefix": { "type": "string", "description": "Identifying prefix only; secrets are never returned." }, "scopes": { "type": "array", "items": { "$ref": "#/components/schemas/Scope" } }, "expiresAt": { "type": [ "string", "null" ], "format": "date-time" }, "lastUsedAt": { "type": [ "string", "null" ], "format": "date-time" }, "createdAt": { "type": "string", "format": "date-time" }, "revokedAt": { "type": [ "string", "null" ], "format": "date-time" }, "kind": { "enum": [ "token", "oauth" ], "description": "`oauth` is the grant behind a connected MCP/OAuth client." }, "oauthClient": { "anyOf": [ { "type": "object", "properties": { "clientId": { "type": "string" }, "name": { "type": "string" } }, "required": [ "clientId", "name" ] }, { "type": "null" } ], "description": "The connected client for `kind: oauth`." } }, "required": [ "id", "name", "prefix", "scopes", "expiresAt", "lastUsedAt", "createdAt", "revokedAt" ] }, "Device": { "type": "object", "properties": { "id": { "type": "string" }, "platform": { "enum": [ "ios", "android" ] }, "deviceName": { "type": [ "string", "null" ] }, "active": { "type": "boolean" }, "liveActivitiesCapable": { "type": "boolean" }, "liveActivityTokenEnvironment": { "enum": [ "sandbox", "production", null ] }, "liveActivityTokenUpdatedAt": { "type": [ "string", "null" ], "format": "date-time" }, "interactiveLiveActivitiesCapable": { "type": "boolean" }, "createdAt": { "type": "string", "format": "date-time" }, "lastSeenAt": { "type": "string", "format": "date-time" } }, "required": [ "id", "platform", "deviceName", "active", "liveActivitiesCapable", "liveActivityTokenEnvironment", "liveActivityTokenUpdatedAt", "interactiveLiveActivitiesCapable", "createdAt", "lastSeenAt" ] }, "AgentService": { "type": "object", "properties": { "id": { "type": "string" }, "title": { "type": "string" }, "imageUrl": { "type": [ "string", "null" ] }, "url": { "type": [ "string", "null" ] }, "createdAt": { "type": "string", "format": "date-time" }, "updatedAt": { "type": "string", "format": "date-time" }, "familyId": { "type": [ "string", "null" ] }, "defaultReplyWebhookId": { "type": [ "string", "null" ] } }, "required": [ "id", "title", "imageUrl", "url", "createdAt", "updatedAt", "familyId", "defaultReplyWebhookId" ] }, "ServiceCreated": { "type": "object", "properties": { "service": { "allOf": [ { "$ref": "#/components/schemas/AgentService" }, { "type": "object", "properties": { "webhookUrl": { "type": [ "string", "null" ] } }, "required": [ "webhookUrl" ] } ] }, "webhookUrl": { "type": "string", "description": "Full webhook URL containing the plaintext token. Returned once." } }, "required": [ "service", "webhookUrl" ] }, "ReplyWebhook": { "type": "object", "properties": { "isDefault": { "type": "boolean" }, "id": { "type": "string", "description": "Saved Reply webhook ID (whk_…)." }, "name": { "type": "string" }, "url": { "type": "string", "format": "uri", "description": "HTTPS destination for signed reply events." }, "preset": { "enum": [ "generic", "grok", "slack" ] }, "headers": { "type": "array", "items": { "type": "object", "properties": { "name": { "type": "string" }, "value": { "type": "string", "description": "Redacted stored header value." } }, "required": [ "name", "value" ] } }, "events": { "type": "array", "items": { "enum": [ "interaction.answered", "interaction.expired", "interaction.canceled" ] } }, "enabled": { "type": "boolean" }, "createdAt": { "type": "string", "format": "date-time" }, "updatedAt": { "type": "string", "format": "date-time" } }, "required": [ "isDefault", "id", "name", "url", "preset", "headers", "events", "enabled", "createdAt", "updatedAt" ] }, "ReplyWebhookCreated": { "type": "object", "properties": { "webhook": { "$ref": "#/components/schemas/ReplyWebhook" }, "signingSecret": { "type": "string", "description": "Plaintext signing secret. Returned only on creation or rotation; store securely." } }, "required": [ "webhook", "signingSecret" ] }, "ReplyWebhookDelivery": { "type": "object", "properties": { "attemptLog": { "type": "array", "items": { "type": "object", "properties": { "attempt": { "type": "integer" }, "attemptedAt": { "type": "string", "format": "date-time" }, "status": { "enum": [ "delivered", "retrying", "failed" ] }, "latencyMs": { "type": "integer" }, "responseCode": { "type": [ "integer", "null" ] }, "error": { "type": [ "string", "null" ] } }, "required": [ "attempt", "attemptedAt", "status", "latencyMs", "responseCode", "error" ] } }, "id": { "type": "string" }, "type": { "enum": [ "interaction.answered", "interaction.expired", "interaction.canceled" ] }, "status": { "enum": [ "pending", "retrying", "delivered", "failed", "canceled" ] }, "attempts": { "type": "integer" }, "responseCode": { "type": [ "integer", "null" ] }, "latencyMs": { "type": [ "integer", "null" ] }, "error": { "type": [ "string", "null" ] }, "createdAt": { "type": "string", "format": "date-time" }, "deliveredAt": { "type": [ "string", "null" ], "format": "date-time" }, "nextAttemptAt": { "type": [ "string", "null" ], "format": "date-time" } }, "required": [ "attemptLog", "id", "type", "status", "attempts", "responseCode", "latencyMs", "error", "createdAt", "deliveredAt", "nextAttemptAt" ] }, "Event": { "type": "object", "properties": { "id": { "type": "string" }, "serviceId": { "type": "string" }, "serviceTitle": { "type": "string" }, "title": { "type": [ "string", "null" ] }, "body": { "type": "string" }, "imageUrl": { "type": [ "string", "null" ] }, "url": { "type": [ "string", "null" ] }, "status": { "type": "string" }, "deliveredCount": { "type": "integer" }, "error": { "type": [ "string", "null" ] }, "createdAt": { "type": "string", "format": "date-time" } }, "required": [ "id", "serviceId", "serviceTitle", "title", "body", "imageUrl", "url", "status", "deliveredCount", "error", "createdAt" ] }, "AgentNotification": { "type": "object", "properties": { "id": { "type": "string" }, "title": { "type": "string" }, "body": { "type": "string" }, "imageUrl": { "type": [ "string", "null" ] }, "url": { "type": [ "string", "null" ] }, "createdAt": { "type": "string", "format": "date-time" }, "projectId": { "type": [ "string", "null" ] }, "summary": { "type": [ "string", "null" ] }, "bodyFormat": { "enum": [ "text", "markdown" ] }, "appId": { "type": "string" } }, "required": [ "id", "title", "body", "imageUrl", "url", "createdAt" ] }, "AgentNotificationCreated": { "type": "object", "properties": { "notification": { "$ref": "#/components/schemas/AgentNotification" }, "accepted": { "type": "integer" }, "idempotent": { "type": "boolean" }, "message": { "type": "string" } }, "required": [ "notification", "accepted" ] }, "AgentNotificationWithdrawn": { "type": "object", "properties": { "ok": { "const": true }, "notificationId": { "type": "string" }, "status": { "enum": [ "withdrawn", "withdraw_partial" ] }, "accepted": { "type": "integer" } }, "required": [ "ok", "notificationId", "status", "accepted" ] }, "Interaction": { "type": "object", "properties": { "id": { "type": "string" }, "title": { "type": "string" }, "prompt": { "type": "string" }, "kind": { "enum": [ "approval", "yes_no", "reply" ] }, "presentation": { "enum": [ "notification", "live_activity" ] }, "status": { "enum": [ "pending", "approved", "denied", "yes", "no", "replied", "canceled", "expired" ] }, "choices": { "type": "array", "items": { "type": "string" } }, "response": { "type": [ "string", "null" ] }, "imageUrl": { "type": [ "string", "null" ] }, "url": { "type": [ "string", "null" ] }, "actionDigest": { "type": "string" }, "primaryLabel": { "type": [ "string", "null" ] }, "secondaryLabel": { "type": [ "string", "null" ] }, "accepted": { "type": "integer" }, "respondingDeviceId": { "type": [ "string", "null" ] }, "respondingUserId": { "type": [ "string", "null" ] }, "expiresAt": { "type": "string", "format": "date-time" }, "createdAt": { "type": "string", "format": "date-time" }, "respondedAt": { "type": [ "string", "null" ], "format": "date-time" }, "canceledAt": { "type": [ "string", "null" ], "format": "date-time" } }, "required": [ "id", "title", "prompt", "kind", "presentation", "status", "choices", "response", "imageUrl", "url", "actionDigest", "primaryLabel", "secondaryLabel", "accepted", "respondingDeviceId", "respondingUserId", "expiresAt", "createdAt", "respondedAt", "canceledAt" ] }, "InboxInteraction": { "allOf": [ { "$ref": "#/components/schemas/Interaction" }, { "type": "object", "properties": { "sourceName": { "type": "string" }, "sourceImageUrl": { "type": [ "string", "null" ] }, "projectId": { "type": [ "string", "null" ] } }, "required": [ "sourceName", "sourceImageUrl" ] } ] }, "InteractionCreated": { "type": "object", "properties": { "interaction": { "$ref": "#/components/schemas/Interaction" }, "accepted": { "type": "integer" }, "idempotent": { "type": "boolean" }, "liveActivityId": { "type": "string" }, "queued": { "type": "integer", "description": "Durably queued device attempts; not provider acceptance or device receipt." }, "message": { "type": "string" } }, "required": [ "interaction", "accepted" ] }, "LiveActivity": { "type": "object", "properties": { "id": { "type": "string" }, "key": { "type": [ "string", "null" ] }, "props": { "type": "object", "description": "LiveActivityProps (title, optional subtitle, status, detail, progress, …)." }, "status": { "enum": [ "starting", "active", "partial", "failed", "ended", "expired" ] }, "sequence": { "type": "integer" }, "accepted": { "type": "integer" }, "failed": { "type": "integer" }, "expiresAt": { "type": "string", "format": "date-time" }, "createdAt": { "type": "string", "format": "date-time" }, "updatedAt": { "type": "string", "format": "date-time" }, "endedAt": { "type": [ "string", "null" ], "format": "date-time" }, "readiness": { "type": "object", "properties": { "ready": { "type": "integer" }, "queuedStarts": { "type": "integer" }, "awaitingUpdateToken": { "type": "integer" }, "warnings": { "type": "array", "items": { "enum": [ "update_token_delayed" ] } }, "nextStartAt": { "type": [ "string", "null" ], "format": "date-time" } }, "required": [ "ready", "queuedStarts", "awaitingUpdateToken", "warnings", "nextStartAt" ] } }, "required": [ "id", "key", "props", "status", "sequence", "accepted", "failed", "expiresAt", "createdAt", "updatedAt", "endedAt", "readiness" ] }, "LiveActivityMutation": { "type": "object", "properties": { "apnsPriority": { "type": "integer", "enum": [ 5, 10 ], "description": "Selected iOS APNs transport priority. Queued work has not yet been sent; Android delivery is unchanged." }, "priorityReason": { "type": "string", "enum": [ "routine", "milestone", "budget_exhausted", "start", "end", "interaction" ] }, "activity": { "$ref": "#/components/schemas/LiveActivity" }, "accepted": { "type": "integer" }, "failed": { "type": "integer" }, "queued": { "type": "integer", "description": "Durably queued device operations awaiting a paced start, iOS update token, or provider retry. Separate from accepted, failed, and superseded outcomes." }, "superseded": { "type": "integer", "description": "Unsent routine progress replaced by a newer queued snapshot; never counted as accepted." }, "replaced": { "type": "integer" }, "idempotent": { "type": "boolean" }, "message": { "type": "string" } }, "required": [ "activity", "accepted", "failed" ] }, "AppSummary": { "type": "object", "properties": { "id": { "type": "string" }, "name": { "type": "string" }, "origin": { "type": "string" }, "iconUrl": { "type": [ "string", "null" ] } }, "required": [ "id", "name", "origin", "iconUrl" ] }, "App": { "allOf": [ { "$ref": "#/components/schemas/AppSummary" }, { "type": "object", "properties": { "storageScope": { "type": "string", "description": "Opaque browser-storage generation, scoped to the viewer, app, origin and workspace membership. Changes require a fresh local partition." }, "url": { "type": "string" }, "projectId": { "type": [ "string", "null" ] }, "projectName": { "type": [ "string", "null" ] }, "shareName": { "type": "boolean" }, "shareEmail": { "type": "boolean" }, "consentedAt": { "type": [ "string", "null" ], "format": "date-time" }, "lastOpenedAt": { "type": [ "string", "null" ], "format": "date-time" }, "createdBy": { "type": [ "string", "null" ] }, "createdAt": { "type": "string", "format": "date-time" }, "updatedAt": { "type": "string", "format": "date-time" }, "team": { "anyOf": [ { "$ref": "#/components/schemas/TeamSummary" }, { "type": "null" } ], "description": "Owning team; null for personal apps. Sharing and consent fields then describe the calling account only." }, "addedBy": { "type": [ "string", "null" ] } }, "required": [ "storageScope", "url", "projectId", "projectName", "shareName", "shareEmail", "consentedAt", "lastOpenedAt", "createdBy", "createdAt", "updatedAt", "team", "addedBy" ] } ] }, "InboxProjects": { "type": "object", "properties": { "projects": { "type": "array", "items": { "type": "object", "properties": { "projectId": { "type": [ "string", "null" ] }, "name": { "type": "string" }, "unreadCount": { "type": "integer" }, "totalCount": { "type": "integer" }, "latestTitle": { "type": [ "string", "null" ] }, "latestPreview": { "type": [ "string", "null" ] }, "latestImageUrl": { "type": [ "string", "null" ] }, "latestAt": { "type": [ "string", "null" ], "format": "date-time" } }, "required": [ "projectId", "name", "unreadCount", "totalCount", "latestTitle", "latestPreview", "latestImageUrl", "latestAt" ] } }, "totalUnread": { "type": "integer" } }, "required": [ "projects", "totalUnread" ] }, "InboxNotificationSummary": { "type": "object", "properties": { "id": { "type": "string", "description": "Composite ID: `event:` or `notification:`." }, "origin": { "enum": [ "event", "notification" ] }, "projectId": { "type": [ "string", "null" ] }, "projectName": { "type": [ "string", "null" ] }, "sourceName": { "type": "string" }, "sourceImageUrl": { "type": [ "string", "null" ] }, "title": { "type": "string" }, "preview": { "type": "string" }, "url": { "type": [ "string", "null" ] }, "bodyFormat": { "enum": [ "text", "markdown" ] }, "readAt": { "type": [ "string", "null" ], "format": "date-time" }, "createdAt": { "type": "string", "format": "date-time" }, "app": { "anyOf": [ { "$ref": "#/components/schemas/AppSummary" }, { "type": "null" } ] } }, "required": [ "id", "origin", "projectId", "projectName", "sourceName", "sourceImageUrl", "title", "preview", "url", "bodyFormat", "readAt", "createdAt", "app" ] }, "InboxNotificationPage": { "type": "object", "properties": { "items": { "type": "array", "items": { "$ref": "#/components/schemas/InboxNotificationSummary" } }, "nextCursor": { "type": [ "string", "null" ] }, "readThroughToken": { "type": "string" } }, "required": [ "items", "nextCursor", "readThroughToken" ] }, "InboxNotificationDetail": { "allOf": [ { "$ref": "#/components/schemas/InboxNotificationSummary" }, { "type": "object", "properties": { "body": { "type": "string" }, "summary": { "type": [ "string", "null" ] }, "status": { "type": [ "string", "null" ] }, "pushReceipts": { "type": "array", "items": { "type": "object", "properties": { "id": { "type": "string" }, "deviceId": { "type": "string" }, "status": { "enum": [ "pending", "accepted", "error", "unknown" ] }, "errorCode": { "type": [ "string", "null" ] }, "checkedAt": { "type": [ "string", "null" ], "format": "date-time" } }, "required": [ "id", "deviceId", "status", "errorCode", "checkedAt" ] } } }, "required": [ "body", "summary", "status" ] } ] }, "ReadMark": { "type": "object", "properties": { "ok": { "const": true }, "readAt": { "type": [ "string", "null" ], "format": "date-time" }, "idempotent": { "type": "boolean" } }, "required": [ "ok", "readAt" ] }, "ActivityFeedPage": { "type": "object", "properties": { "items": { "type": "array", "items": { "type": "object", "properties": { "id": { "type": "string" }, "kind": { "enum": [ "notification", "live_activity", "response" ] }, "sourceName": { "type": "string" }, "sourceImageUrl": { "type": [ "string", "null" ] }, "title": { "type": "string" }, "detail": { "type": [ "string", "null" ] }, "url": { "type": [ "string", "null" ] }, "result": { "type": [ "string", "null" ] }, "createdAt": { "type": "string", "format": "date-time" } }, "required": [ "id", "kind", "sourceName", "sourceImageUrl", "title", "detail", "url", "result", "createdAt" ] } }, "page": { "type": "integer" }, "pageSize": { "type": "integer" }, "total": { "type": "integer" } }, "required": [ "items", "page", "pageSize", "total" ] }, "TeamSummary": { "type": "object", "properties": { "id": { "type": "string" }, "name": { "type": "string" } }, "required": [ "id", "name" ] }, "Team": { "type": "object", "properties": { "kind": { "enum": [ "family", "team" ] }, "id": { "type": "string" }, "name": { "type": "string" }, "role": { "$ref": "#/components/schemas/TeamRole" }, "memberCount": { "type": "integer" }, "appCount": { "type": "integer" }, "oncallGroupCount": { "type": "integer" }, "deviceGroupCount": { "type": "integer" }, "verifiedDomain": { "type": [ "string", "null" ] }, "pendingDomain": { "type": [ "string", "null" ] }, "domainVerification": { "anyOf": [ { "type": "object", "properties": { "host": { "type": "string" }, "value": { "type": "string" } }, "required": [ "host", "value" ] }, { "type": "null" } ], "description": "TXT challenge; returned only to the owner while a domain is pending." }, "autoJoinVerifiedDomain": { "type": "boolean" }, "retentionDays": { "type": [ "integer", "null" ] }, "seats": { "type": "object", "properties": { "used": { "type": "integer" }, "available": { "type": [ "integer", "null" ] }, "billable": { "type": "integer" } }, "required": [ "used", "available", "billable" ] }, "plan": { "enum": [ "free", "team" ] }, "createdAt": { "type": "string", "format": "date-time" } }, "required": [ "id", "name", "role", "memberCount", "appCount", "oncallGroupCount", "deviceGroupCount", "verifiedDomain", "pendingDomain", "domainVerification", "autoJoinVerifiedDomain", "retentionDays", "seats", "plan", "createdAt" ] }, "TeamRole": { "type": "string", "enum": [ "owner", "admin", "member" ] }, "TeamMember": { "type": "object", "properties": { "userId": { "type": "string" }, "name": { "type": "string" }, "email": { "type": "string" }, "image": { "type": [ "string", "null" ] }, "role": { "$ref": "#/components/schemas/TeamRole" }, "joinedAt": { "type": "string", "format": "date-time" } }, "required": [ "userId", "name", "email", "image", "role", "joinedAt" ] }, "TeamInvite": { "type": "object", "properties": { "id": { "type": "string" }, "teamId": { "type": "string" }, "email": { "type": [ "string", "null" ] }, "domain": { "type": [ "string", "null" ] }, "role": { "$ref": "#/components/schemas/TeamRole" }, "invitedBy": { "type": "string" }, "expiresAt": { "type": "string", "format": "date-time" }, "acceptedAt": { "type": [ "string", "null" ], "format": "date-time" }, "revokedAt": { "type": [ "string", "null" ], "format": "date-time" }, "createdAt": { "type": "string", "format": "date-time" } }, "required": [ "id", "teamId", "email", "domain", "role", "invitedBy", "expiresAt", "acceptedAt", "revokedAt", "createdAt" ] }, "TeamInviteCreated": { "type": "object", "properties": { "invite": { "$ref": "#/components/schemas/TeamInvite" }, "code": { "type": "string", "description": "Plaintext join code, returned once." }, "url": { "type": "string", "description": "Join link a person opens to accept (accepting is human-only)." } }, "required": [ "invite", "code", "url" ] }, "DeviceGroupMember": { "type": "object", "properties": { "userId": { "type": "string" }, "name": { "type": "string" }, "email": { "type": "string" }, "image": { "type": [ "string", "null" ] } }, "required": [ "userId", "name", "email", "image" ] }, "DeviceGroupDevice": { "type": "object", "properties": { "id": { "type": "string" }, "deviceName": { "type": [ "string", "null" ] }, "userId": { "type": "string" }, "userName": { "type": "string" } }, "required": [ "id", "deviceName", "userId", "userName" ] }, "DeviceGroup": { "type": "object", "properties": { "id": { "type": "string" }, "teamId": { "type": "string" }, "name": { "type": "string" }, "members": { "type": "array", "items": { "$ref": "#/components/schemas/DeviceGroupMember" } }, "devices": { "type": "array", "items": { "$ref": "#/components/schemas/DeviceGroupDevice" } }, "createdAt": { "type": "string", "format": "date-time" }, "updatedAt": { "type": "string", "format": "date-time" } }, "required": [ "id", "teamId", "name", "members", "devices", "createdAt", "updatedAt" ] }, "AuditLogEntry": { "type": "object", "properties": { "id": { "type": "string" }, "teamId": { "type": "string" }, "actorUserId": { "type": [ "string", "null" ] }, "actorName": { "type": [ "string", "null" ] }, "actorTokenId": { "type": [ "string", "null" ] }, "action": { "type": "string" }, "targetType": { "type": [ "string", "null" ] }, "targetId": { "type": [ "string", "null" ] }, "metadata": { "type": [ "object", "null" ] }, "ip": { "type": [ "string", "null" ] }, "userAgent": { "type": [ "string", "null" ] }, "createdAt": { "type": "string", "format": "date-time" } }, "required": [ "id", "teamId", "actorUserId", "actorName", "actorTokenId", "action", "targetType", "targetId", "metadata", "ip", "userAgent", "createdAt" ] }, "AuditLogPage": { "type": "object", "properties": { "entries": { "type": "array", "items": { "$ref": "#/components/schemas/AuditLogEntry" } }, "nextCursor": { "type": [ "string", "null" ] } }, "required": [ "entries", "nextCursor" ] }, "OncallPerson": { "type": "object", "properties": { "userId": { "type": "string" }, "name": { "type": "string" }, "image": { "type": [ "string", "null" ] } }, "required": [ "userId", "name", "image" ] }, "OncallShift": { "type": "object", "properties": { "person": { "$ref": "#/components/schemas/OncallPerson" }, "startsAt": { "type": "string", "format": "date-time" }, "endsAt": { "type": "string", "format": "date-time" }, "override": { "type": "boolean" }, "overrideId": { "type": "string" } }, "required": [ "person", "startsAt", "endsAt", "override" ] }, "OncallGroup": { "type": "object", "properties": { "id": { "type": "string" }, "teamId": { "type": "string" }, "name": { "type": "string" }, "rotation": { "type": "object", "properties": { "members": { "type": "array", "items": { "$ref": "#/components/schemas/OncallPerson" } }, "period": { "enum": [ "daily", "weekly" ] }, "handoffAt": { "type": "string" }, "timezone": { "type": "string" }, "startsAt": { "type": "string", "format": "date-time" } }, "required": [ "members", "period", "handoffAt", "timezone", "startsAt" ] }, "escalation": { "type": "array", "items": { "type": "object", "properties": { "afterMinutes": { "type": "integer" }, "target": { "enum": [ "next", "group" ] } }, "required": [ "afterMinutes", "target" ] } }, "current": { "anyOf": [ { "$ref": "#/components/schemas/OncallShift" }, { "type": "null" } ] }, "upcoming": { "type": "array", "items": { "$ref": "#/components/schemas/OncallShift" } }, "overrides": { "type": "array", "items": { "type": "object", "properties": { "id": { "type": "string" }, "person": { "$ref": "#/components/schemas/OncallPerson" }, "startsAt": { "type": "string", "format": "date-time" }, "endsAt": { "type": "string", "format": "date-time" } }, "required": [ "id", "person", "startsAt", "endsAt" ] } }, "openPageCount": { "type": "integer" }, "createdAt": { "type": "string", "format": "date-time" }, "updatedAt": { "type": "string", "format": "date-time" } }, "required": [ "id", "teamId", "name", "rotation", "escalation", "current", "upcoming", "openPageCount", "createdAt", "updatedAt" ] }, "OncallPage": { "type": "object", "properties": { "id": { "type": "string" }, "groupId": { "type": "string" }, "groupName": { "type": "string" }, "teamId": { "type": "string" }, "title": { "type": "string" }, "body": { "type": [ "string", "null" ] }, "url": { "type": [ "string", "null" ] }, "app": { "anyOf": [ { "$ref": "#/components/schemas/AppSummary" }, { "type": "null" } ] }, "status": { "enum": [ "triggered", "acknowledged", "resolved" ] }, "dedupKey": { "type": [ "string", "null" ] }, "repeatCount": { "type": "integer" }, "notified": { "type": "array", "items": { "$ref": "#/components/schemas/OncallPerson" } }, "escalationStep": { "type": "integer" }, "nextEscalationAt": { "type": [ "string", "null" ], "format": "date-time" }, "acknowledgedBy": { "anyOf": [ { "$ref": "#/components/schemas/OncallPerson" }, { "type": "null" } ] }, "acknowledgedAt": { "type": [ "string", "null" ], "format": "date-time" }, "resolvedBy": { "anyOf": [ { "$ref": "#/components/schemas/OncallPerson" }, { "type": "null" } ] }, "resolvedAt": { "type": [ "string", "null" ], "format": "date-time" }, "source": { "type": "string" }, "createdAt": { "type": "string", "format": "date-time" } }, "required": [ "id", "groupId", "groupName", "teamId", "title", "body", "url", "app", "status", "dedupKey", "repeatCount", "notified", "escalationStep", "nextEscalationAt", "acknowledgedBy", "acknowledgedAt", "resolvedBy", "resolvedAt", "source", "createdAt" ] }, "OncallPageCreated": { "type": "object", "properties": { "page": { "$ref": "#/components/schemas/OncallPage" }, "deduplicated": { "type": "boolean" }, "accepted": { "type": "integer" } }, "required": [ "page", "deduplicated", "accepted" ] }, "Billing": { "type": "object", "properties": { "configured": { "type": "boolean" }, "plan": { "const": "free", "description": "Individuals and Families are free. Teams signup is not available yet." }, "priceMonthly": { "type": "number" }, "features": { "type": "object", "properties": { "deviceRouting": { "type": "boolean" } }, "required": [ "deviceRouting" ] }, "limits": { "type": "object", "properties": { "devices": { "type": [ "integer", "null" ] }, "notificationsPerMonth": { "type": [ "integer", "null" ] }, "servicePerMinute": { "type": "integer" }, "accountPerMinute": { "type": "integer" } }, "required": [ "devices", "notificationsPerMonth", "servicePerMinute", "accountPerMinute" ] }, "usage": { "type": "object", "properties": { "notificationsRemaining": { "type": [ "integer", "null" ] } }, "required": [ "notificationsRemaining" ] } }, "required": [ "configured", "plan", "priceMonthly", "features", "limits", "usage" ] }, "ConnectorRoute": { "type": "object", "properties": { "name": { "type": "string" }, "target": { "type": "string" }, "url": { "type": "string" }, "appId": { "type": "string" }, "enabled": { "type": "boolean" } }, "required": [ "name", "target", "url" ] }, "Connector": { "type": "object", "properties": { "id": { "type": "string" }, "enabled": { "type": "boolean" }, "name": { "type": "string" }, "tunnelId": { "type": "string" }, "hostname": { "type": "string" }, "routes": { "type": "array", "items": { "$ref": "#/components/schemas/ConnectorRoute" } }, "status": { "enum": [ "online", "offline", "unknown" ] }, "lastSeenAt": { "type": [ "string", "null" ], "format": "date-time" }, "teamId": { "type": [ "string", "null" ] }, "oncallGroupId": { "type": [ "string", "null" ] }, "createdAt": { "type": "string", "format": "date-time" }, "updatedAt": { "type": "string", "format": "date-time" } }, "required": [ "id", "enabled", "name", "tunnelId", "hostname", "routes", "status", "lastSeenAt", "teamId", "oncallGroupId", "createdAt", "updatedAt" ] }, "ConnectorCreated": { "type": "object", "properties": { "connector": { "$ref": "#/components/schemas/Connector" }, "callbackSecret": { "type": "string", "description": "HMAC secret for verifying callback signatures. Returned once on create." }, "reused": { "type": "boolean" } }, "required": [ "connector" ] } } } ```