# Notifications > Read the shared inbox, change preferences and inspect delivery outcomes. Section: Synapse and Locus. Web page: https://docs.katara.com/products/notifications/ Katara's notification API provides one personal inbox across account, organization, billing, provider, Cortex, Synapse and Locus events. It uses your verified application session. API keys cannot read the inbox or change a person's notification preferences. Delivery providers and their configuration belong to each deployment. An event or channel appearing in the catalog does not mean every related feature or delivery provider is active where you are. Connected sources report terminal Synapse runs, document ingestion, evaluation outcomes, pending Locus approval stages, account and billing changes, changes to a provider's on-chain registration, and agent grants, budgets and API keys that end soon. Approval notices are limited to currently eligible reviewers. Two catalog entries have no source yet: `cortex.usage_limit` and `synapse.connection_required`. Evaluation notices distinguish an available uncalibrated judgment (`synapse.evaluation_ready`), a known failure (`synapse.evaluation_failed`) and an unresolved call (`synapse.evaluation_unknown`). A ready notice can include criteria that could not be scored. Open the [assessment](https://docs.katara.com/products/evaluations.txt) to inspect the canonical result and run outcome; a notice alone does not establish a pass, calibration or charge outcome. Current source, requester and reviewer authority controls visibility. Losing access can hide a notice without changing the recorded score or charge. `synapse.online_evaluation_skipped` goes to the person who turned [online evaluation](https://docs.katara.com/products/evaluations.txt#judging-live-traffic) on for an agent, at most once a day for each reason: runs were drawn and not judged because the day's cap was reached (`daily_cap`) or the budget could not cover them (`budget`). The setting stays on. `locus.connection_required` reports a committed tool-account revocation or a committed OAuth state requiring reconnection. It belongs to the Locus `connections` category and requires an in-app notice. Its `resource_id` identifies the connection; `source_operation_id` identifies the exact immutable credential version. The event contains no provider account details, tokens or account digest. Open the connection and inspect its current state before reconnecting. Source authority is checked again when reading or delivering the notice; the event does not authorize a retry or provider exchange. `synapse.connection_required` describes a run waiting for a connection, and no run waits on one, so nothing sends it. A bounded private collector also reports expired refresh reservations and retired credential or OAuth profiles when they currently require reconnection. It does not finalize or retry a provider exchange. Expired, revoked, superseded and reset versions are evaluated before a notice is made available; a stale notice does not prove that reconnection is still appropriate. Local producer checks do not prove external notification delivery, provider feedback or permission to retry an unknown delivery. ## Provider notices Provider notices come from the on-chain provider directory. When a provider's entry changes, the account whose wallet registered it is told: | Change | Notice | | --- | --- | | The provider is flagged | `provider.health_attention` | | The provider registers, pauses or resumes, or its wallet, service key or service address changes | `provider.registration_changed` | | The payout address changes | `provider.payment_updated` | A notice reaches the account only while that account still holds the wallet. The first reading of a chain's directory is its starting point, so turning this on does not announce providers that already existed. ## Expiry reminders Agent grants, budgets, API keys and repository approvals each end at a time chosen when they were made. When 80% of that time has passed, Katara sends one reminder, so a 30-day grant is announced on day 24. | What ends | Notice | Who is told | Where it is renewed | | --- | --- | --- | --- | | An agent grant | `locus.grant_expiring` | The person who issued it, and the organization's owners and admins | The agent's **New Run** page, which offers **Renew**; a grant that does not start runs opens under **Budgets & Grants** | | A budget | `locus.budget_expiring` | The person who funded it, and the organization's owners and admins | **Budgets & Grants** | | An API key | `organization.key_expiring` | The person who created it | **API Keys** | | A repository's approval | `synapse.repository_approval_expiring` | The person who attached the repository, and the organization's owners and admins | **Repositories**, which offers **Renew** | Grants issued together for one agent on one budget, ending at the same moment, send one reminder between them, as when you allow an agent to run. The budget made with them sends none of its own: renewing the agent renews both. No reminder is sent for anything revoked, closed or already ended, or for anything renewed. A grant counts as renewed when a later grant for the same agent and purpose ends after it. A budget counts as renewed when its payer funded a newer one for the same kind of usage that ends later, and every agent on the old budget already draws on the new one; a budget no agent draws on is always announced. A key counts as renewed when its holder created a newer key with the same name in the same environment that lasts longer. A repository's approval counts as renewed when someone renewed it; none is sent for a repository that was detached, or whose installation was removed or uninstalled. If any of this happens after the reminder was sent, the reminder leaves the inbox and its queued deliveries are dropped. The reminders are in the `expiry` category of Locus, Synapse and Organization, with no required channel, so you can turn each channel off, delay it or batch it like any optional notice. An expiry item also carries `subject`: the object's `name`, its `expires_at` and the portal `path` where it is renewed. The subject is read from the source each time you read the notice; nothing in the notice record stores the name. Emails include the same name, end time and link. ## Read the inbox Use `listNotifications` / `list_notifications` without filters for your cross-product inbox. Filter by organization, environment, product or state. An environment filter must include its organization. States are `unread`, `read` and `archived`. Pages contain up to 100 items. Follow `next_cursor` when present, even if a page contains no visible items. Access can change while paging; the service checks current resource authority before returning a notice. `getNotificationCount` returns `unread_count` and `has_more`. When `has_more` is true, display the count as a lower bound, such as **1,000+**, rather than an exact total. The count can be filtered by organization, environment and product. The title and body use fixed notification text. Customer prompts, model replies and tool arguments are not included. Open the authenticated `deep_link` to inspect the current resource. Receiving a notification does not grant access to that resource or approve an action. ## Mark an item read or archive it Fetch the current item, then call `setNotificationState` with its ID, desired state and current version. Use `set_notification_state` in Python. A stale version receives a conflict; reload before deciding whether to repeat the change. After a lost response, read the item to find out whether the update committed. Archiving a notice does not cancel a run, reject an approval or stop another delivery channel. An approval notification leads to the exact action review in Locus; the notification itself has no approve button in its payload. ## Set preferences `getNotificationCatalog` lists valid product/category pairs and required channels. `listNotificationPreferences` returns saved preferences for the selected account, organization or environment scope. More specific optional preferences take precedence over broader ones. A preference names its product, category, channel, enabled state, IANA time zone and delivery interval. Supported intervals are `immediate`, `hourly` and `daily`. Quiet-hour start and end values are minutes after local midnight, from 0 to 1,439; provide both values, and keep them different. A snooze can extend at most 30 days into the future. Use version zero when creating a preference. Use the returned current version when updating it. Required notices override optional preferences for the channels listed in the catalog. Required security notifications cannot be disabled, delayed or snoozed through these settings. The schema includes in-app, email, webhook, push and SMS channel preferences. Saving a preference does not configure a delivery destination or activate a provider. Optional hourly and daily deliveries can combine due notices into a digest. Each notice retains its own delivery record, and grouped records expose the same `batch_id`. Current access and preferences are checked again before sending. ## Interpret delivery records `listNotificationDeliveries` returns channel, state, attempt count, next attempt time and a bounded result code. It does not expose recipient addresses or delivery credentials. | State | Meaning | | --- | --- | | `pending`, `sending` | Delivery is queued or in progress | | `accepted` | A provider accepted the message; recipient delivery is not yet confirmed | | `delivered` | Delivery was confirmed; it does not mean the recipient read it | | `failed` | A temporary failure may be retried within the delivery limit | | `dead_letter` | Delivery needs attention, including unavailable channels or exhausted attempts | | `unknown` | The send outcome is uncertain; the system does not assume it is safe to resend | | `suppressed` | Current access, preferences or recipient state prevented delivery | | `intercepted` | A Test-environment external delivery was captured instead of being sent | Test interception applies to external deliveries for Test events. In-app notices remain available for inspection. Use delivery records to verify Test behavior without assuming an email, webhook, push message or SMS reached an external recipient. ## Email events The email provider reports what happened to each message it accepted, and the delivery record follows: - **Delivered.** The delivery becomes `delivered`. - **Bounced for good, or refused because the provider already knows the address fails.** The delivery becomes `dead_letter` and email to you stops until you confirm a different address. A temporary bounce changes nothing, because the provider is still trying. - **Reported as spam.** Email to you stops until you turn an email preference back on. - **Failed.** The provider gave up on that one message. The delivery becomes `dead_letter`; later email is unaffected. Each report is recorded once, however often the provider sends it. ## Text messages A text goes only to a phone number you confirmed, and only after you turn SMS on for yourself. Installations send texts through Amazon SNS. SNS reports delivery to the installation's own logs rather than back to Katara, so an accepted text stays `accepted`. ## Limits Katara sends one person at most 20 messages a minute on each channel. A single producer fans out at most 1,000 notices a minute in one environment, or 10,000 a minute across account notices. A notice over a limit waits for the next minute; none are dropped. ## Set up a signed webhook A webhook belongs to one signed-in organization administrator and one environment. Create it with an HTTPS destination you control and an explicit list of event types. The API family is `/katara/v1/notification-webhooks`; the SDK exposes `createNotificationWebhook` / `create_notification_webhook` and related methods. 1. Generate an ID and preserve the exact creation body after an uncertain response. Setup returns the ownership document, verification URL, challenge expiry and a one-time signing secret. Store the secret in your receiving service; the API will not reveal it again on a retry. 2. Serve the returned ownership document at the exact verification URL. Select **Verify** to request an ownership check. This reads the document; it does not send a notification. 3. Review the destination, environment and event types. Submit the returned current version and `review_digest` to activate that exact configuration. 4. Verify signatures in your receiving service before accepting a payload. Keep the ownership document available: delivery checks continued control of the endpoint. Setup changes require a person's signed-in session and current organization administrator access. API keys cannot administer a person's webhook. Test events are intercepted rather than sent to the endpoint. Verification, review, revocation and signing-key rotation use the current subscription version. Reload after a conflict. After an uncertain rotation, fetch current metadata rather than repeating the rotation automatically; a lost new secret requires another explicitly reviewed rotation. Existing delivery attempts keep their original signing-key version. The TypeScript and Python SDKs snapshot webhook creation, webhook review and preference updates before resolving session credentials. The transmitted body keeps the reviewed destination, event list, scope, digest and version even if the caller's object changes while authentication is pending. These operations do not use a caller idempotency header, and neither SDK retries them automatically. After a lost preference response, reload preferences in the original scope and compare the saved version and settings before reviewing another update. After a lost webhook review response, inspect that exact subscription before deciding whether a new review is needed. Do not substitute a newer version or digest into an uncertain request. Recovery metadata must exclude the one-time signing secret; keep that secret only in the receiving service's protected configuration. The signing secret is an unpadded URL-safe base64 encoding of 32 bytes. Decode it before computing HMAC-SHA256. The signed bytes are the ASCII Unix timestamp, a dot, the delivery ID, another dot, and the exact raw JSON request body. `Katara-Signature` contains `t=,v1=`. `Katara-Key-Id` selects the key; `Katara-Delivery-Id` and `Idempotency-Key` contain the delivery ID. Compare signatures in constant time, reject timestamps older than five minutes or more than 30 seconds ahead, and deduplicate the delivery ID before applying an effect. Return HTTP 200 or 202 with JSON `{"delivery_id":"","status":"accepted"}` after accepting the delivery. This confirms receipt, not completion of your downstream work. HTTP 429 or 503 with the same ID and `"status":"retry"` asserts that no effect occurred and permits a bounded retry. HTTP 400, 401, 403 or 410 with the same ID and `"status":"rejected"` is permanent. An uncertain send after bytes may have reached the receiver is recorded as `unknown` and is not automatically resent. Arbitrary error replies and invalid acknowledgements also leave the outcome unknown. ## Named operator recovery Delivery recovery requires a separate, scoped operator enrollment provisioned by a trusted administrator of the installation. Organization ownership does not grant this permission. Enrollments identify one human account, an exact environment and reset generation, a fixed version, and an explicit expiry. Revocation is one-way. The provisioner's database identity and server time are recorded privately. A signed-in operator can review a delivery with its enrollment ID and version. The service checks current organization membership, recipient and source access while producing the review. Only a dead-letter delivery, or the leader of its frozen digest, can be explicitly queued again. Unknown sends remain ineligible. Test external delivery stays intercepted. `client.notificationRecovery.deliveries(scope, enrollmentID, version)` lists the dead-lettered deliveries in the environment, newest first, up to 25 a page. Each is checked as its review would be, and one whose recipient may no longer receive the event is left out, so a page can be short while `next_cursor` is present. Use `client.notificationRecovery.review(scope, deliveryID, enrollmentID, version)` in TypeScript, then explicitly call `redeliver(scope, review, operationUUID)` after reviewing the returned metadata. Python exposes `client.notification_recovery`. The operation UUID is the request's idempotency key. Preserve it and the exact review after an uncertain reply. Use `receipt(scope, operationUUID, review)` to inspect completion before an explicit same-request retry. Changed delivery, enrollment, scope or reset pins cannot reuse that operation. The portal's **Notification Recovery** page stores only the reviewed identifiers, versions, states and timestamps in tab storage, scoped to the signed-in account and environment. It never retries automatically. A historical receipt records a queue transition; it does not prove current delivery or current operator access. Reading it still requires current account and tenant visibility. New recovery must remain authorized through transaction completion, including time spent waiting for conflicting requests. A recovery operator works in one environment. A notice to your own account belongs to no organization, and a notice whose fan-out ran out of attempts reached nobody, so neither has an operator of its own. Katara support resends those under a [support access](https://docs.katara.com/products/support-access.txt) grant for your environment or your account: the grant names the reason and a deadline, and each look at the dead letters and each resend is on its record. A resent delivery goes back into its channel's queue, where access, preferences and suppressions are checked again before anything is sent. A resent push goes to the devices you have now, not the ones the first attempt found.