Skip to content

feat: manage webhooks, login hooks, custom claims and audit sinks from the CLI - #359

Merged
Bccorb merged 2 commits into
mainfrom
feat/webhooks-and-hooks-commands
Nov 8, 2026
Merged

Bccorb merged 2 commits into
mainfrom
feat/webhooks-and-hooks-commands

Conversation

@Bccorb

@Bccorb Bccorb commented Nov 8, 2026 •

Copy link
Copy Markdown
Contributor

Needs auth API v0.23, which is not released. Every command here calls routes from seamless-auth-api #580 (webhooks), #582 (audit sinks), #583 (login hooks) and #584 (custom claims). Those PRs are open and the API release is held (seamless-auth-api#566), so nothing here has run against a real instance. Against v0.22 and earlier each command exits 1 with "This instance has no webhooks. Webhooks need Seamless Auth API v0.23 or later." (and the same for login hooks, custom claims and audit sinks), as #356 does for permissions.

Part of #343 (webhooks and hooks). With #356 this covers the issue's list, plus custom claims and audit sinks so the whole v0.23 admin API can be driven from the CLI.

What

Four instance-management commands, all with --json:

seamless webhooks list | get <id>
seamless webhooks create <url> --events <type,...> [--description] [--disabled]
seamless webhooks update <id> [--url] [--events] [--description | --no-description]
seamless webhooks enable <id> | disable <id>
seamless webhooks delete <id> [--force]
seamless webhooks rotate-secret <id> [--force]
seamless webhooks test <id>
seamless webhooks deliveries [--endpoint <id>] [--status pending|succeeded|failed|dead] [--event <type>] [--limit <n>] [--cursor <c>]
seamless webhooks delivery <deliveryId> | redeliver <deliveryId>

seamless hooks list | get <trigger>
seamless hooks set <post_login|pre_token> [--url] [--timeout <ms>] [--failure-mode open|closed] [--enable | --disable]
seamless hooks delete <trigger> [--force] | rotate-secret <trigger> [--force] | test <trigger>
seamless hooks invocations <trigger> [--outcome <outcome>] [--limit <n>]

seamless claims list | get <kind> <name>
seamless claims set <instance_role|organization_role|oidc_client> <name> (<json> | --file <path>) [--force]
seamless claims delete <kind> <name> [--force]

seamless audit-sinks list | get <id>
seamless audit-sinks create <name> --kind otlp --endpoint <url> [--header Name=ENV_VAR]... [--service-name]
seamless audit-sinks create <name> --kind syslog --host <h> [--port] [--transport tls|tcp] [--app-name] [--facility] [--tls-*-env]
seamless audit-sinks update <id> [--name] [--enable | --disable] [settings] | delete <id> [--force] | test <id>

How

  • Same structure as feat: manage permissions and organization roles from the CLI #356: client functions in src/core/admin.ts use call(), so every write (including test and redeliver, which the API treats as writes) gets the step-up retry and a 403 goes through forbidden(). unsupported() now takes the message, so each feature names itself on a pre-v0.23 instance.
  • Responses are parsed with @seamless-auth/types 0.40.0 schemas (bumped from 0.38), loosened. The event catalogue, the failure reasons and the audit sink kinds grow by addition, so those fields accept any string: a newer API does not break an older CLI's list. Failure reasons print as the API's code, so misconfigured (a sink setting that cannot be used, such as an unset _AUDIT_SINK_SECRET env var) shows as failing: misconfigured and Test record failed: misconfigured, which a test covers.
  • A 400 from the API's own schema check carries details.issues; those are added to the message (Could not update webhook (400: invalid_request), url must use https.).
  • Signing secrets are printed once, when issued: webhooks create, rotate-secret on both, and hooks set when it creates the hook. --json includes them.
  • Rotation keeps the old secret signing for 24 hours, so it does not ask. A second rotation inside that window drops the older secret at once, so it reads the current record and asks first (--force skips).
  • hooks set and audit-sinks update start from the current record. The API replaces a hook's settings whole and fills defaults, so without this hooks set pre_token --disable would also reset the timeout and failure mode. A new hook gets a note that it fails closed.
  • claims set replaces the whole set like the API. It reads the current set and asks before removing a claim, as roles set does. Reserved names, bad names, nesting past 3 and the 1 KiB budget are refused before the request, with readable messages for the name rules.
  • Ids are checked as UUIDs before the request. A failed test exits 1, with or without --json.
  • Help topics, README, the AGENTS.md command list, and a minor changeset. These features add no system config keys, so WRITABLE_KEYS is untouched.

Verification (at 8f662a0)

  • npm run coverage (also the pre-commit hook): 88 files passed, 2 skipped; 2104 tests passed, 28 skipped. Statements 99.04%, branches 96.78%, functions 98.47%, lines 99.32%, above the floor. The four new command files are at 100% lines.
  • npm run build passes. npm run check:types-current: @seamless-auth/types "^0.40.0" is current (latest 0.40.0). npm run check:pins passes.
  • Tests use mocked responses shaped from the route files and controllers on feat/custom-claims and feat/audit-sinks.

Not run: no live run against an instance, since the API PRs are unreleased and unmerged. No committed e2e, same as #356.

…m the CLI

Adds seamless webhooks, seamless hooks, seamless claims and
seamless audit-sinks over the auth API v0.23 admin routes. Signing
secrets are printed once, when issued. A second secret rotation inside
the 24 hour overlap, deletes, and claim removals ask first. hooks set and
audit-sinks update change only what a flag names. A failed test call
exits 1. An instance before v0.23 answers with a message naming the
release it needs.

Part of #343
@Bccorb
Bccorb marked this pull request as ready for review November 8, 2026 03:30
@Bccorb
Bccorb merged commit 7b2d4ea into main Nov 8, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant