Skip to content

Block reference — Notification & Integration

Notification blocks reach outside the platform. They send email, dispatch multi-channel messages, POST to partner webhooks, append structured entries to the audit log, and pause execution for a measured beat. While an Auth or Risk block does its work entirely inside the platform, a Notification block crosses a network boundary — or at minimum produces a side effect that outlives the flow.

Two things to know before you wire one up. First, outbound HTTP calls (Call Webhook) are SSRF-guarded: the platform refuses requests to private CIDR ranges (RFC 1918), cloud metadata endpoints, and localhost, regardless of what URL you supply. Second, your tenant's outbound URL allowlist applies to Call Webhook; if a domain isn't on it, the call is blocked before it leaves. Both controls are there to protect your users' data, not to slow you down — adding a domain to the allowlist takes thirty seconds in your tenant's Security settings.

  • "POST, PUT, or GET a partner endpoint and capture the response for downstream branching" — Call Webhook
  • "Trigger a multi-channel message (email + SMS + in-app) via one config" — Notify
  • "Queue a templated transactional email via the courier adapter" — Send Email
  • "Send a password-recovery email with a time-limited link" — Password Recovery Send
  • "Append a structured entry to the audit log" — Log Event
  • "Pause the flow for N milliseconds before the next block runs" — Delay

Block id: call_webhook  ·  Available in: All flows

Send an HTTPS request to an external URL and capture the response. URL, headers, and body support {{ expr }} templates, so you can inject anything from state — the user's email, the risk score, the application's client ID — directly into the request. The response body is auto-parsed as JSON and exposed under step.<your-slug>.body.*, so downstream Decision blocks can branch on any field the partner returns.

This block is the workhorse for arbitrary HTTP integrations: fraud scoring, CRM sync, entitlement checks, Slack alerts, custom identity enrichment. Anything you can hit over HTTPS, you can integrate here without writing JavaScript.

Reads from state

  • Any state path you reference in the URL, headers, or body template — for example, {{ event.user.email }} or {{ step.risk-eval.score }}

Writes to state

  • step.<slug>.status_code — HTTP status code returned by the webhook, e.g. 200, 4xx, 5xx
  • step.<slug>.body_preview — first 64 KB of the response body
  • step.<slug>.body_truncatedtrue when the response body exceeded the 64 KB cap
  • step.<slug>.body.<path> — individual JSON leaf values from the response (dynamic keys; auto-parsed)
  • step.<slug>.render_error — set only when a template substitution failed

Use when — you need to call any HTTPS endpoint and either act on the response or simply fire-and-forget a side effect (for example, notifying a Slack channel that a new user registered).

Cautions — The platform sets a 5-second timeout on outbound calls. Design your partner endpoint to respond within that window; the block fails open on timeout by default (the flow continues). Never POST real user PII to public testing endpoints — anyone with the URL can read those requests.


Call Webhook is the most-used Notification block, so it deserves a closer look at the pieces that make it composable.

Templated fields. Every text field — URL, each header value, and the request body — accepts {{ expr }} syntax. Click the {} picker next to any field to browse everything referenceable: event.* paths, step.<slug>.* outputs from upstream blocks, and session.* after Issue Session. For example:

URL: https://api.cymmetri.internal/risk-check
Body: { "user_id": "{{ event.user.id }}", "ip": "{{ event.request.ip }}", "risk": {{ step.risk-eval.score }} }

Idempotency key. The platform injects an Idempotency-Key header on every Call Webhook request, set to the stage token's jti. If your partner endpoint supports idempotency keys, retries are safe.

Response access. After the block runs, step.<slug>.status_code holds the HTTP status. JSON response bodies are parsed into step.<slug>.body.* — one key per leaf value. If the partner returns { "score": 0.82, "label": "fraud" } and you named the block fraud-check, you access step.fraud-check.body.score and step.fraud-check.body.label in any downstream block's expression or template.

Branching on the response. Drop a Decision block after Call Webhook and write an expression like step.fraud-check.body.score > 0.7. Route that branch to a Deny terminal or an MFA challenge; route the default branch to Issue Session.

Limits. HTTPS only. 5-second timeout. 64 KB response cap (body_truncated is true beyond that). SSRF guard blocks RFC 1918, link-local, and cloud metadata addresses. The tenant outbound allowlist applies.


Block id: notify  ·  Available in: All flows

Dispatch a notification via the platform's notification dispatcher. One block config selects a template and a delivery channel (email, sms, or in_app); the dispatcher handles routing to the underlying courier. The flow continues to the next block on success. This block is the right choice when you have a tenant-wide notification template already set up and you want to trigger it from a flow step — for example, sending a welcome email at the end of a registration flow or an account-lock warning when brute force fires.

Reads from state

  • event.user.id — identifies who receives the notification
  • Template variables you reference inside the notification template

Writes to state

  • step.<slug>.template_id — ID of the notification template that was rendered
  • step.<slug>.channel — channel used: email, sms, or in_app
  • step.<slug>.senttrue when the dispatcher successfully queued the notification

Use when — you have a pre-built notification template and want to trigger it at a specific point in a flow without configuring a full outbound email block.

Cautions — The block fails open: if the dispatcher is unconfigured or the courier is unreachable, the flow continues and sent is false. Check step.<slug>.sent in a downstream Decision block if delivery confirmation matters for your policy.


Block id: send_email  ·  Available in: All flows

Queue an outbound email via the courier adapter. You pick a template ID and, optionally, a locale; the courier resolves the template, renders it, and hands it off for delivery. The block is fail-open when the courier is unconfigured — useful during development when you haven't wired a production mail provider yet.

Reads from state

  • event.user.email — the address the email is sent to
  • event.user.id — used to look up the recipient identity for template personalisation

Writes to state

  • step.<slug>.queuedtrue when the email was successfully handed to the courier for delivery

Use when — you want to send a specific transactional email at a flow step — a login-notification, a welcome message, a security alert — using one of your pre-built templates.

Cautionsqueued: true means the courier accepted the message; it does not guarantee delivery. Check your courier's delivery logs for bounce tracking. Like all outbound blocks, this one fails open so a courier outage doesn't break your login flow.


Block id: password_recovery_send  ·  Available in: Password Recovery

Dispatch a password-recovery email containing a time-limited token. The block mints the token, embeds it in the recovery link, and hands the email to the courier. Configure token_ttl_seconds to control how long the link stays valid. Pair with a Password Update block downstream — after the user clicks the link and submits a new password, Password Update consumes the token and commits the change.

This block belongs in the Password Recovery flow and nowhere else. It assumes the user's identity has already been located (an Identity Lookup block upstream) and the email address is known.

Reads from state

  • event.user.id — the identity the recovery email is sent for
  • event.user.email — the address the recovery link is sent to

Writes to state

  • step.<slug>.recovery_token — time-limited token included in the recovery email link
  • step.<slug>.recovery_token_expires — ISO 8601 timestamp after which the token is no longer valid

Use when — you're building or customising a password-reset flow and need the platform to send the recovery email and mint the token.


Block id: log_event  ·  Available in: All flows

Emit a structured audit event via the platform's event bus. The block writes nothing to flow state — it has no outputs for downstream blocks to read. Its only purpose is to append a labelled, structured entry to the audit log at exactly the point in the flow where you place it.

This block always passes through, regardless of what you configure. It never denies or halts a flow.

Reads from state

  • (no declared inputs — you configure the event type and message statically in the block settings)

Writes to state

  • (no declared outputs)

Use when — you want a named audit marker at a specific flow step: "consent accepted", "risk threshold exceeded and MFA challenged", "new device first login". These entries show up in your tenant's audit log alongside the platform's own events.


Block id: delay  ·  Available in: All flows

Introduce an artificial pause before the next block runs. You configure a duration in milliseconds; the block respects the request's context deadline, so it never causes the flow to exceed its timeout budget even if the configured delay is larger than the remaining deadline. The maximum configurable delay is 5 seconds.

Reads from state

  • (no declared inputs)

Writes to state

  • step.<slug>.completed_ms — milliseconds the block actually waited, capped by the request context deadline

Use when — you want to smooth rate-limit bursts between two rapid flow submissions, add deliberate pacing before a retry-sensitive external call, or insert a constant-time delay to prevent timing-based user enumeration attacks.

Cautions — Even a 500 ms delay adds up across multiple flow stages. Measure your end-to-end flow latency before deploying delays to production. The 5-second cap is a hard limit set by the platform — it cannot be raised in block config.


Place Call Webhook in your Post-Registration stage. Configure it to POST the new user's identity traits to your CRM's contact-create endpoint. The flow continues whether or not the CRM call succeeds (fail-open). If you need to branch on failure, add a Decision block after Call Webhook checking step.<slug>.status_code != 200. See the Sync new user to CRM recipe for a worked example including the full block config.

Place Call Webhook in your Post-Auth stage to POST to a fraud-scoring partner. Follow it with a Decision block:

  • When step.fraud-check.body.score > 0.7 — route to MFA or Deny
  • Default — route to Issue Session

See the Fraud webhook branch recipe for the complete flow layout and sample webhook response shapes.

The fastest way to confirm the platform is sending exactly what you expect is to point Call Webhook at a public inspection service like webhook.site during development. It captures every request so you can inspect headers, body, and query parameters in real time.

Keep two things in mind. First, public inspection services are open by default — anyone who obtains the URL can read the requests you send. Never POST real user data (emails, names, identifiers) to a public inspector. Use synthetic test values only. Second, remember to swap the URL back to your real endpoint before publishing the flow.