---
title: "Update Notify configuration"
method: PUT
path: "/products/notify/config"
tags: ["notify"]
---

# Update Notify configuration

`PUT /products/notify/config`

Deep-merges a partial patch onto the stored overrides, so one category can be written without resending the rest. Send a category key with a null value to delete it.

## Request body

- NotifyConfigPatch — Partial update, deep-merged onto the stored overrides. Unknown sections and fields are rejected. `channel_credentials` is owner-managed and cannot be set.
  - `delivery` object
    - `default_channels` string[]
    - `channel_priority` object
    - `realtime_fallback_delay` object
      - `email_minutes` integer
      - `sms_minutes` integer
      - `voice_minutes` integer
    - `escalation_enabled` boolean
  - `reliability` object
    - `rate_limit_per_recipient_per_minute` integer
  - `history` object
    - `history_ttl_days` integer
    - `receipt_retention_days` integer
  - `security` object
    - `verification_level` 'none' | 'identifier' | 'passkey'
    - `reveal_freshness_minutes` integer
    - `mandatory_seal_categories` string[]
  - `events` NotifyEventsConfig — Outbound event webhook. Events are POSTed as JSON with an `X-SparkVault-Signature` header carrying the hex HMAC-SHA256 of the raw body under `secret`. Best effort — one retry, then dropped — so polling remains the source of truth. Nothing is sent until both fields are stored.
    - `webhook_url` string, uri — https only, on a public host. Loopback, private, link-local, and CGNAT addresses are rejected.
    - `secret` string — Never returned on a read. A response reports `secret_set` instead.
    - `secret_set` boolean — Whether a signing secret is stored. Replaces `secret` on every read.
  - `categories` object — Keyed topic map. A null value deletes the topic. New keys must match ^[a-z0-9_]{1,40}$. The reserved `security` category can be neither deleted nor made optional.

## Response `200`

The updated configuration.

- object
  - `data` NotifyConfigResponse, required
    - `effective` NotifyEffectiveConfig, required — Platform defaults with the account overrides applied.
      - `delivery` object
        - `default_channels` string[]
        - `channel_priority` object — Per-category channel order.
        - `realtime_fallback_delay` object
          - `email_minutes` integer
          - `sms_minutes` integer
          - `voice_minutes` integer
        - `escalation_enabled` boolean
      - `reliability` object
        - `rate_limit_per_recipient_per_minute` integer — Delivery ceiling per recipient per minute, enforced at fan-out. Cannot be disabled.
      - `history` object
        - `history_ttl_days` integer
        - `receipt_retention_days` integer
      - `security` object
        - `verification_level` 'none' | 'identifier' | 'passkey'
        - `reveal_freshness_minutes` integer — 0 means off.
        - `mandatory_seal_categories` string[] — Categories that may never be sent plaintext.
      - `events` NotifyEventsConfig — Outbound event webhook. Events are POSTed as JSON with an `X-SparkVault-Signature` header carrying the hex HMAC-SHA256 of the raw body under `secret`. Best effort — one retry, then dropped — so polling remains the source of truth. Nothing is sent until both fields are stored.
        - `webhook_url` string, uri — https only, on a public host. Loopback, private, link-local, and CGNAT addresses are rejected.
        - `secret` string — Never returned on a read. A response reports `secret_set` instead.
        - `secret_set` boolean — Whether a signing secret is stored. Replaces `secret` on every read.
      - `categories` object
      - `channel_credentials` object — Owner-managed provider wiring. Read-only.
    - `overrides` object, required — The stored delta verbatim, including category delete tombstones.
  - `meta` ResponseMeta — Envelope metadata present on every successful response.
    - `api_version` string
    - `request_id` string
    - `response_ms` integer
    - `timestamp` integer — Unix seconds.
    - `pools` object — Storage and bandwidth capacity snapshot for the account.
    - `billing` object
      - `past_due` boolean
    - `quota` object — This minute of the account request budget. Present on authenticated success responses that passed the throttle; absent otherwise.
      - `limit` integer
      - `used` integer
      - `remaining` integer
      - `resets_at` integer — Unix seconds.

## Other responses

- `400` — VALIDATION_ERROR — the message names the offending field.
- `401` — AUTHENTICATION_ERROR — missing or invalid credentials.
- `403` — FORBIDDEN — the caller may not act on this account.
- `429` — RATE_LIMIT_EXCEEDED — the account exceeded 300 requests this minute. `Retry-After` carries the seconds to wait, and `details` carries limit/resets_at, plus `used` whenever the request was counted. A request refused because the limiter itself was unavailable reports no `used`.

---

[API](https://skmtc.dev/sparkvault/apis/sparkvault-api.md) · [All operations](https://skmtc.dev/sparkvault/apis/sparkvault-api/llms.txt) · [OpenAPI document](https://skmtc-service-production.skmtc.workers.dev/v1/apis/sparkvault/sparkvault-api/revisions/aaf3fd4981d9/schema)
