Skip to content

Channels and Delivery

Channels are where inbox policy lives: who may send, what a message must carry, and how delivery behaves. Creating a channel requires a current tenant administrator or live service principal, plus creation authority in the chosen workspace. A regular organization member cannot create channels just by claiming an admin role in a JWT. Channel reads and lists follow workspace/channel reader rules, including explicit grants and their expiry or revocation.

Creating channels

snug inbox channel create --name docs4.alerts \
  --description "Docs guide channel" \
  --actions-allowed --scheduled-delivery-allowed
snug inbox channel list
snug inbox channel get --channel-id 0199e100-0000-7000-8000-00000000000b

The created channel (captured live) - note the server assigns a random channel_id, which is what every send and filter refers to, not the name:

{
  "channel_id": "0199e100-0000-7000-8000-00000000000b",
  "name": "docs4.alerts",
  "workspace_id": "root",
  "allowed_producers": [],
  "default_priority": "normal",
  "retention_days": 90,
  "actions_allowed": true,
  "technical_notes_required": false,
  "delivery_policy": {
    "pubsub_topic": null,
    "scheduled_delivery_allowed": true,
    "websocket_fanout": false
  },
  "created_by": "docs-wave-admin", ...
}

Use --workspace-id (HTTP workspace_id) when creating a channel in a named workspace; omission selects the account root. Share channel reader/editor/manager access through /api/v1/access/inbox-channels/{channel_id}/grants. Editor grants allow producing; manager grants permit sharing and bypassing the producer allowlist. None grants access to another recipient's messages, counts, threads or actions.

There is no delete or update endpoint - channel policy is fixed at creation. channel list speaks the standard list contract: q over name and description (GET /api/v1/inbox/channels?q=docs4, or -q docs4), filters on default_priority, actions_allowed, technical_notes_required, created_by, created_at, and updated_at, and sorts on created_at (newest first by default) and name.

Policy gates on send

Each gate below applies to a restricted channel created with --producer svc-docs4 --technical-notes-required:

  • Producer allowlist - a sender first needs channel editor/manager or workspace creation authority. An empty allowed_producers list adds no extra restriction. A nonempty list admits only the listed principal IDs or channel managers; other producers receive 403 producer_not_allowed. Sender identity and kind come from the verified actor and cannot be supplied as a privilege flag.
  • Technical notes - with technical_notes_required, a send must include technical notes with a non-empty summary (CLI --notes, HTTP technical_notes.summary) or it fails with 400 technical_notes_required. Notes are structured handoff context: summary, risk level, open questions, successor hint.
  • Actions gate - attaching actions on a channel without actions_allowed fails with 400 actions_not_allowed.
  • Scheduled delivery gate - a future deliver_at on a channel without scheduled_delivery_allowed fails with 400 scheduled_delivery_not_allowed.

Scheduled delivery

On a channel that allows it, a future deliver_at returns "status": "scheduled" and the message stays invisible to the recipient until due:

snug inbox send --to user_123 --channel 0199e100-0000-7000-8000-00000000000b \
  --title "Scheduled ping" --msg "See you soon" \
  --deliver-at 2026-08-28T05:04:01Z

Promotion is automatic: the server runs delivery workers on a roughly one-second tick. The message was absent from the list immediately after sending and present a few seconds after its due time, with no manual step.

Expiry and self-destruct

Two per-message controls, both HTTP-only fields on send:

  • expires_at - the message is swept once the timestamp passes.
  • self_destruct_after_read_seconds - a countdown that starts when the message is read. With a 2-second fuse: after reading and waiting, the message was not deleted but redacted - title replaced with [self-destructed], body emptied, and the message auto-archived.

The same background workers that promote scheduled messages run these sweeps continuously.

Manual maintenance

POST /api/v1/inbox/maintenance/run (CLI: snug inbox maintenance) force-runs one round of the promotion, expiry, and self-destruct sweeps and reports what it did for the current tenant. It requires live tenant administrator authority; a regular member receives 403 insufficient_permissions:

{ "promoted": 0, "expired": 0, "self_destructed": 0 }

Since the server sweeps on its own, this endpoint is for operations - forcing a sweep in tests or after bulk imports - not something clients need in normal use.

Thread retention

Thread summaries record delivery history. Their message count and highest priority remain after messages expire or are deleted. The latest-message ID identifies the last delivery and may no longer resolve after deletion. Unread counts are computed from the currently retained unread messages. Thread list filters, sorting and pagination run in PostgreSQL.

Self-destruction redacts the latest title in a matching thread summary in the same transaction as the message. Deleting the latest message replaces that summary title with [deleted]. An older message's deletion or self-destruction never overwrites a newer delivery's title. The summary's delivery counts remain intact.