Skip to main content
The Triggers surface reads your declared webhooks, schedules, and OAuth straight from your definition and lets you run them in fixtures or live mode. Set definition on the registry entry and the surface shows a card per webhook and schedule, plus an OAuth declaration card and the Local OAuth panel. Without definition, the surface explains what to add.

Setup

asHarnessDefinition() erases the storage generic so the definition can sit in the heterogeneous registry array. The mock context is built from the definition’s own storage schemas and validates every read and write against them at runtime.

Webhooks

Each declared webhook gets a card, titled with its id, with a payload editor seeded from examplePayload and a Deliver webhook button:
On the Triggers surface:
  1. Edit the JSON payload in the editor
  2. Click Deliver webhook
  3. The payload is validated against payloadSchema; an invalid one never reaches the handler
  4. Your real handler runs against the mock (fixtures) or live context, and the result appears below the card
Signature verification is logged as skipped — it is the transport’s job in every environment. A webhook that declares signature.scheme shows it as a badge on its card (algorithm, signed payload, keyed pairs, timestamp source and replay window), so you can read back exactly what the host ingress will compute. See Webhooks.

Schedules

Each declared schedule gets a card, titled with its id and showing its cron, with a Run now button:
Run now invokes the handler with trigger: 'manual'; the host scheduler uses trigger: 'schedule', so your handler must be safe under both.

OAuth

A declared oauth gets an OAuth declaration card showing:
  • Authorization URL and token URL
  • Declared scopes
  • PKCE (yes/no)
  • The client id and client secret secret names
  • Egress coverage: one badge per endpoint origin, marked as failing if your egress does not cover it
Below that, the Local OAuth panel (shown in both modes, so you can set up before switching) walks you through:
  1. Creating an app in your vendor’s console and adding the redirect URI (with a Copy button)
  2. Enabling the declared scopes
  3. Pasting the credentials and saving them
The checklist is derived from your declaration. Add oauth.setup to the harness entry for what it cannot know — where the console is and what the provider calls each key:

Fixtures mode

In fixtures mode, the harness answers oauth/status from the registry entry’s oauth.connected field, so an OAuth widget renders with zero traffic and you do not hand-write that fixture. It defaults to connected. Pass a boolean, or a per-variant map:

Live mode

In live mode, the shell runs the real authorize → callback → onTokens loop through your developer app credentials. See Live Mode.

triggerMocks

The mock context is built from your definition’s schemas and seeded from triggerMocks:

storage

Storage rows per scope, validated against your definition’s storage schemas. Keep them fake: everything in harness.config.ts is compiled into the browser bundle.

secrets

Secrets by tier for the fixtures-mode mock context. account is writable through ctx.secrets.set(); admin stands in for admin-issued credentials and rejects writes. In live mode, admin secrets come from .ekanos/local-secrets.json instead.
The harness logs a console warning when an entry seeds triggerMocks.secrets, because those literals are compiled into the browser bundle and the .next build output.

fetchHandlers

Only for responses that must be dynamic or stateful. Your fixtures already answer ctx.fetch (see Fixtures), so reach for handlers only when you need something that changes. A string match matches URLs that start with it; a RegExp is tested against the full URL. Handlers are tried before fixtures and win for the URLs they match. A handler call that nothing answers gets the same NoRecordedResponseError a widget gets, rather than createMockContext()’s default empty 200.
Do NOT re-author your vendor payloads as fetch handlers. That was the double-authoring HTTP fixtures exist to kill — the same data in two shapes with nothing relating them. Use fixtures for everything that’s not dynamic; handlers only when responses have to change per-request or carry state.

onActivate

The onActivate hook runs on the Activation surface, not the Triggers surface. Submitting the activation form there runs your real hook (if declared) against the context the toolbar mode selects—browser mock in fixtures, live context in live—and shows the outcome (ran, skipped because not declared, or threw with the message) below the connect dialog.

Invocation results

When a webhook or schedule runs, the card shows a result panel: a summary badge (with detail, if any), the handler’s logs, and the account and user storage after the run. In fixtures mode every invocation runs in the browser against one mock context per visit, seeded from triggerMocks, so state accumulates across invocations; leaving the page resets it. In live mode, the buttons post to /api/integrations/<slug>/trigger and handlers run on the server-held live context, which persists until the dev server restarts.

Troubleshooting

Webhook validation fails

Check your payload against the payloadSchema. An invalid payload never reaches the handler.

Handler error: no recorded response

A handler’s ctx.fetch call didn’t match a fixture. See Fixtures for how to record it. The error message shows the URL to paste (with credentials redacted).

Payload editor JSON is invalid

Check for trailing commas, missing quotes, or unescaped special characters.

Triggers surface is empty

Your entry has no definition field. Add it with asHarnessDefinition(myDefinition).

Egress coverage badge is red

Your oauth.provider.authorizationUrl or tokenUrl origin is not in your definition’s egress. Add it or check for typos.