Monitoring
Joryio's Monitoring section lets you set up alerts that fire when something you care about crosses a threshold - your inbound API traffic, the webhooks Joryio delivers from your Journeys, the customer events your app and SDK track, or your email deliverability rates (bounces, complaints, delivery). Each alert watches a single metric, evaluates it every minute, and notifies you by email or webhook when it transitions from healthy to triggered.
Open Settings → Logs & Monitoring in the dashboard to manage alerts and audit past fires.
The Logs & Monitoring hub
Monitoring lives inside the unified Logs & Monitoring hub, reached from a single sidebar entry. The hub's left rail groups four areas:
| Area | What it shows |
|---|---|
| Alerts | Every alert rule in your workspace, its runtime status, and quick actions (snooze / pause / edit / delete / duplicate). |
| History | Append-only log of every time an alert transitioned to or from a firing state. Used for post-incident review. |
| Error logs | Delivery and processing errors across channels, for debugging failed sends. |
| Audit | Admin actions, delivery errors, and authentication events - see Audit Log. |
This page covers Alerts and History (the alerting feature). A bell in the dashboard header surfaces recent fires across the workspace with an unread count, so you don't have to keep the hub open to notice an incident.
Creating an alert
Click + New alert on the Alerts page. The New Alert form is a 3-step wizard with a sticky left rail tracking your progress:
| Step | Title | What you set |
|---|---|---|
| 1 | Metric - What to watch | Pick a quick-start template (or build your own), name the alert, then choose the direction and metric. |
| 2 | Condition - When it fires | Choose absolute vs change comparison, set the threshold, optionally scope the alert, and watch a live preview. |
| 3 | Notify - Who hears about it | Review a summary, pick the notification channel (email or webhook), and set the cooldown + enabled state. |
You can click back to any completed step from the rail. Next is gated - you can't advance past step 1 until the alert has a name, and the footer shows a live status pill (Would fire now / Healthy / Paused) from step 2 onward.
Step 1 - Metric
Start from a template
Pre-built templates cover the most common scenarios across three groups. Picking one pre-fills every step - you can still tweak anything before saving. Click Clear to reset to a blank build.
Inbound - API calls into Joryio:
| Template | Fires when |
|---|---|
| Integration silent failure | Total calls drop ≥80% vs the previous hour. Catches a deploy that broke your integration. |
| Auth broken | More than 100 × 401 Unauthorized responses in 5 minutes. Catches a deploy that invalidated an API key. |
| Server rejecting payloads | More than 100 × 5xx responses in 5 minutes. Usually means your request format changed and Joryio can no longer parse it. |
| Traffic spike | Total calls go up 200% vs the previous hour. Catches a runaway script or unexpected surge. |
Outbound - webhooks Joryio sends from your Journeys:
| Template | Fires when |
|---|---|
| Webhook endpoint failing | More than 50 × 5xx responses on webhook deliveries in 5 minutes. Your endpoint is down or the path no longer exists. |
| Webhook stopped firing | Webhook deliveries drop ≥90% vs the previous hour. Journey paused, audience drained, or the queue is stuck. |
Events - funnel drops in your tracked events:
| Template | Fires when |
|---|---|
| Purchase funnel drop | purchase_complete events drop ≥50% vs the same day last week. |
| Add-to-cart drop | add_to_cart events drop ≥30% vs the same hour last week. |
| Signup drop | signup events drop ≥50% vs the same day last week. |
| Active user drop | Unique users firing events drop ≥40% vs the same day last week. |
The Events templates compare against the same window last week rather than the immediately-previous window. Customer events have strong weekly (and hourly) seasonality - a Sunday-night dip is normal, not an incident - so comparing to the previous hour would false-alarm constantly. See Compare to below.
Deliverability - email/SMS health rates:
| Template | Fires when |
|---|---|
| High bounce rate | Bounce rate is above 5% over the last 24h. List quality or sender-reputation issue. |
| High complaint rate | Complaint rate is above 0.1% over the last 24h. Stay below ISP spam thresholds. |
| Low delivery rate | Delivery rate drops below 95% over the last 24h. Fewer messages are reaching inboxes. |
Basics
| Field | What it does |
|---|---|
| Alert name | Required. Shown in the dashboard, in the triggered email subject / webhook payload, and in the History log. You can't advance to step 2 until this is filled in. |
| Description | Optional. Shown to teammates browsing alerts and in the email body so on-call knows what the alert is about. |
Direction
Pick which stream the alert watches:
- Inbound API - counts requests into Joryio's REST API (e.g. your servers calling
POST /events). - Outbound webhooks - counts webhook deliveries out of Joryio (sent from a webhook node in a User Journey).
- Events - counts the customer events tracked from your SDK/app as they land in Joryio. This watches the stored event count, not the inbound API call count - the two diverge when Joryio accepts a call but rejects the payload (that case belongs to Inbound API).
- Deliverability - watches your email/SMS health rates (bounce, complaint, delivery, etc.) measured as a percentage of messages sent over a lookback window. Unlike the count-based directions, deliverability is always absolute mode (a threshold on the rate) and has no scope filter - it covers all messages sent from the workspace.
The metric labels below adapt to the direction you pick.
Metric
Inbound / Outbound:
| Metric | What it measures | When to use |
|---|---|---|
| Calls / Deliveries by response code | Count of calls filtered to specific HTTP status codes you pick. | Error-rate alerts (401, 429, 5xx). |
| Total calls / deliveries | Count of all calls regardless of status. | "Did my integration stop firing?" / "Is traffic surging?" alerts. |
| Requests / Deliveries per second | Mean throughput over the evaluation window. | Per-key or per-endpoint capacity alerts. |
Events:
| Metric | What it measures | When to use |
|---|---|---|
| Event count | Count of a single event (e.g. purchase_complete). | The classic funnel-drop alert - pair with Change mode + same-window-last-week. |
| Total events | Count of every event tracked across all event names. | Catches your SDK going silent entirely (a deploy broke init, the app crashed for a cohort). |
| Unique users | Distinct users firing the selected event (or any event if no event name is picked). | Audience-side issues vs power-user spikes - better than raw count. |
Deliverability (always absolute mode, threshold is a percentage; pick a lookback window of 1h / 4h / 24h / 7d):
| Metric | What it measures | When to use |
|---|---|---|
| Bounce rate | Bounced ÷ sent. | The headline inbox-placement signal - alert above ~2–5%. |
| Complaint rate | Spam complaints ÷ sent. | Mailbox providers throttle above ~0.1–0.3% - alert early. |
| Hard / Soft bounce rate | Permanent vs transient bounces ÷ sent. | Isolate reputation-damaging hard bounces from transient soft bounces. |
| Unsubscribe rate | Unsubscribes ÷ sent. | Content/frequency fatigue. |
| Delivery rate | Delivered ÷ sent. | Use the Below operator to fire when delivery drops under a healthy floor (e.g. < 95%). |
Deliverability alerts need volume to be meaningful: if no messages were sent in the lookback window, the alert never fires (so a delivery rate < 95% alert won't false-alarm on a quiet night).
Response codes (inbound/outbound, when applicable)
When the metric is by response code, pick which HTTP statuses count. The picker groups codes into:
- Buckets -
2xx,4xx,5xx, or all codes. - Success -
200,201,202,204. - Client errors -
400,401,403,404,422,429. - Server errors -
500,502,503,504.
You can mix buckets and specific codes (e.g. "5xx or 429").
Step 2 - Condition
Compare to
Two evaluation modes:
| Mode | Behavior |
|---|---|
| Absolute | Fires when the metric crosses a fixed value for a sustained window. "More than 100 errors in 5 minutes." |
| Change over time | Fires when the metric increases or decreases by a % (or absolute amount) vs an earlier window. "Calls dropped 80% vs the previous hour." |
Absolute mode fields:
- Above / Below - direction of the threshold.
- Threshold value - numeric value the metric must cross.
- For X duration - the metric must stay across the threshold for this entire window before the alert fires. Options: 1m, 5m, 10m, 30m, 1h.
Change mode fields:
- Increased by / Decreased by - direction of the change.
- Threshold value - the % or absolute amount of change.
- % or abs - interpret the threshold as a percentage or absolute count.
- vs previous X window - the size of the comparison window. Options: 15m, 1h, 4h, 1d, 7d.
- Compare to - what to compare the current window against (see below). Only shown in Change mode.
Compare to (seasonal comparison)
In Change mode, the alert can compare the current window against one of four baselines:
| Option | Compares current window against… | Best for |
|---|---|---|
| Typical day like this one (recommended) | The median of the same window 7, 14, 21 and 28 days back. | Almost everything, and percent-drop alerts in particular. Handles weekly seasonality, and because it is a median, one big day cannot move it. |
| Immediately previous | The window right before. | API/webhook traffic with no strong time-of-day pattern, and short windows on steady traffic. |
| Same window last week | The same slot exactly 7 days ago. | Weekly seasonality when you want a strict like-for-like day - but it is a single sample. |
| Multi-day average | The mean of the same window over the last N days (2-30). | Smoothing noisy series where no single day is exceptional. |
Why the median matters. The other three all break in the same way: they let one unusual day set the baseline. Compare against yesterday and every Monday looks like a crash after a quiet Sunday. Compare against the same day last week and a campaign, an article or Black Friday on that day makes today look broken. Even the multi-day average is a mean, so one exceptional day lifts the baseline for the whole averaging window - meaning every normal day after a good day reads as a drop.
A median of four same-weekdays has neither problem: the spike is one sample of four, so it cannot move the middle value, and the samples are all the same weekday, so seasonality never enters. A genuine, sustained change in traffic still becomes the new baseline within a couple of weeks.
If a workspace is too young to have two of those four weeks, the alert reports not enough history yet and stays quiet rather than comparing against zero.
Scope (optional)
Narrow the alert to a specific slice:
- Inbound - limit by API key (specific bearer token) and/or endpoint (specific route).
- Outbound - limit by webhook URL. The dropdown is populated from the live webhook nodes in your active and draft Journeys, so you pick a real destination rather than typing a URL.
- Events - limit by event name. The picker is driven by your workspace's actual event vocabulary (top 200 event names by frequency over the last 30 days, shown with their counts). Leave it on "all events" to count everything; it's disabled when the metric is Total events.
Leave the scope at its "all" default to evaluate the whole workspace.
Live preview
The footer polls a preview endpoint with your current settings and shows:
- Would fire now (red) - the current metric value already crosses the threshold. Useful for catching configs that are too sensitive.
- Healthy (green) - the current value is within the threshold.
- Paused (gray) - the Enabled toggle (on step 3) is off, so even if it would fire it won't.
The preview never saves anything and never sends a notification. It's just a sanity check.
Step 3 - Notify
A dark review summary at the top recaps what you're alerting on ("You're alerting on …"), with an Edit link back to step 1.
Channel
Pick how the alert reaches you when it fires:
Email channel (default):
| Field | What it does |
|---|---|
| Email recipients | Press Enter or comma after typing an address. Click ✕ to remove. At least one recipient is required. |
| One message is sent per recipient on the healthy → triggered transition. Resolves are not emailed (too noisy for an on-call inbox - resolution is visible in Settings → Logs & Monitoring). |
Webhook channel:
| Field | What it does |
|---|---|
| Webhook URL | Required. Joryio POSTs a JSON payload here every time the alert fires. |
| Signing secret | Optional. Joryio signs each request with HMAC-SHA256(body) and sends it in the X-Joryio-Signature header so your receiver can verify the call is genuinely from Joryio. |
A live payload preview is rendered in a dark code block, populated with the alert name and metric you just configured. Unlike email, the webhook channel POSTs on both transitions (triggered and resolved) so your receiver can match incidents end-to-end. Delivery is retried up to 3 times with exponential backoff. See the Monitoring API reference for the exact payload contract.
Cooldown & enabled
| Field | What it does |
|---|---|
| Cooldown | Minimum time between re-fires after the metric returns to healthy. Default 10 minutes. Options: 5m, 10m, 30m, 1h. |
| Enabled | Toggle. When off, the alert is saved in Paused state - it won't evaluate or fire. |
Alert lifecycle
Once saved, every alert sits in one of four states:
| State | Visual | Meaning |
|---|---|---|
| Healthy | Teal pill | Watching, no breach. |
| Triggered | Red pill with pulse | The metric crossed the threshold and the alert just fired. Recipients have been notified. |
| Snoozed | Gray pill | Temporarily silenced. Will auto-resume to Healthy when the snooze window expires. |
| Paused | Gray pill | Indefinitely silenced. Will not evaluate or fire until you manually resume. |
Evaluation cadence
The backend evaluator runs every minute (the smallest duration you can pick in the UI is 1 minute, so finer would be wasted CPU).
Each tick, for every enabled alert that's not snoozed or paused:
- Query the metric source (inbound API request log, outbound webhook delivery log, or the events table) for the duration window.
- Compare the current value to the threshold. In Change mode, also query the comparison window - either the immediately-previous window or the same window 7 days ago.
- If it crosses → set status to Triggered, write a
firingrow to History, notify via the chosen channel. - If it was triggered and the value returns to healthy → set status to Healthy, mark the most recent firing row as
resolvedwith aresolvedAttimestamp (and POST aresolvedwebhook if the channel is webhook).
Cooldown semantics
After a fire, the alert won't re-fire until the cooldown window has elapsed and the metric has crossed back below the threshold and back above it. This avoids paging on every minute-by-minute jitter when a problem is still ongoing.
Snooze vs. pause
- Snooze is timed (1 hour, 4 hours, 24 hours, until tomorrow 9am). The alert auto-resumes when the window passes. Use this for "I know about this, ack me out for 4h".
- Pause is indefinite. The alert stays paused until you manually click Resume. Use this for rules that aren't relevant today (e.g. during a planned maintenance window with no traffic).
You can snooze a single alert from the row's ⋯ kebab menu, or click Snooze all in the red banner when multiple alerts are triggered at once.
Triggered notifications
Email
When an email-channel alert fires, every recipient gets a message with:
- The alert name in the subject (prefixed with an alert icon).
- The current metric value (the one that breached).
- The threshold that was crossed.
- The alert description if you set one.
- A link back to Settings → Logs & Monitoring → Alerts.
The email is sent through your workspace's configured email provider (the same one used for campaigns).
If your workspace has no email provider, the alert still transitions state and writes to History, but no email goes out. Set up an email provider under Settings → Email Configurations before relying on email alerts in production.
Webhook
When a webhook-channel alert transitions, Joryio POSTs a JSON payload to your URL on both triggered and resolved. If you set a signing secret, verify the X-Joryio-Signature header (lowercase hex HMAC-SHA256 of the raw body) before trusting the call. The exact payload shape is documented in the Monitoring API reference.
History
The History page is the audit trail. Every transition (healthy → triggered, triggered → resolved) writes a row.
Each row shows:
- Time - when the transition happened, with relative time underneath.
- Alert + state pill -
Firing,Resolved, orSnoozed at fire. - Metric snapshot - what the metric reached at the moment of the transition, and what the threshold was.
- Mini sparkline - a small chart of the metric around the transition. Red for firing, teal for resolved, gray for snoozed.
- Duration - how long the incident lasted (
Ongoingfor still-firing). - Recipients - who got notified, with
+Nfor the remainder. Reads silent in italic if no one was notified (e.g. the alert was snoozed at the moment).
Filters
Three filters at the top of the page:
- Alert - show events for a single alert rule.
- State -
Firing now,Resolved, orSnoozed at fire. - Period -
24h,7d,30d,90d.
The four stat tiles above the timeline aggregate over the visible (filtered) events:
| Tile | What it counts |
|---|---|
| Firing now | Open incidents - the metric is still breaching. |
| Resolved (7d) | Incidents that self-healed or were acknowledged in the last 7 days. |
| Mean time to resolve | Average duration across resolved incidents. |
| Notifications sent | Total recipients notified across all events in the window. |
What gets measured
The metrics come from three append-only data streams populated automatically.
Inbound API request log
- What's in it: Every API-key-authenticated request to the Joryio REST API. Timestamp, organization, workspace, API key prefix, HTTP method, canonical endpoint (e.g.
/users/:id), status code, duration. - What's NOT in it: Dashboard traffic (JWT-authenticated calls from the Joryio UI), health checks, internal-only routes.
Outbound webhook delivery log
- What's in it: Every webhook attempt sent from a User Journey's webhook node. Timestamp, organization, workspace, canvas, URL, response status (or
0for transport-level errors like timeouts), duration, attempt number. - What's NOT in it: Webhooks sent before this feature was deployed (older queued jobs don't carry the necessary tenant metadata; they're soft-skipped by the logger).
Events
- What's in it: Every customer event tracked from your SDK/app, by event name and user. This is the same
eventstable the rest of the platform reads.
Logging controls & retention
Both the inbound API request log and the outbound webhook delivery log are per-organization configurable (managed by Joryio staff in the admin console):
- Kill switch - each log stream can be toggled on or off independently for an org. When off, no rows are written and metrics for that direction stop updating. Both default to on.
- Retention - each stream has its own retention window between 7 and 365 days (default 90). Rows are deleted automatically once they exceed their org's retention.
If your alerts suddenly stop evaluating for a direction, check with your Joryio admin that the corresponding log stream is still enabled.
Automatic health alerts (zero-config)
Beyond the alerts you configure yourself, Joryio continuously watches every workspace for integration breakage - no setup required:
- Flatline - events were flowing and suddenly stopped entirely (a dead SDK key, a broken deploy, a removed snippet). Raised as critical.
- Sustained drop - event volume falls far below the workspace's own multi-day baseline. Raised as a warning.
- Per-event detection - your most significant events (e.g.
purchase,page_view) are watched individually, so a single broken funnel step is caught even while total volume looks normal.
When something breaks you'll see a banner on the Monitoring page and an item in the notification bell, and receive an email that includes a short AI diagnosis - what most likely broke and what to check first. If you've already configured your own alert covering the same signal, Joryio suppresses the duplicate email.
Dismissing: an automatic alert can be dismissed from the banner - that acknowledges this incident and hides it. If the integration recovers and later breaks again, a new alert is raised. Recovery also resolves the alert automatically.
Cooldown & rate-limit safety
Alerts fire at most once per cooldown window. If a metric stays in breach for hours, you'll get one notification - not 60 per hour. The cooldown clock resets when the metric returns to healthy.
If you've snoozed an alert, fires that happen during the snooze still write a History row (so you can see what you missed), but no notification is sent and the row reads silent (snoozed) in the Recipients column.
Limitations
- Channels: Email and webhook are both supported. There is no native Slack channel - route Joryio webhook alerts into Slack via a Slack incoming webhook, or use email-based incident integrations (e.g. PagerDuty).
- Per-event detail pages: The View details button on history rows is visible but currently a no-op.