Resources · Documentation
Everything you need to start tracking automation reliability. One endpoint, no SDK required.
Leaf Loom works by receiving a webhook POST from your automation tool each time a workflow runs. Your API key is generated when you sign up. Paste the endpoint URL into Zapier, Make, n8n, or any tool that supports webhooks.
POST /api/webhook/log/{user_id}?key={api_key}
Your user ID and API key are both found in the Automations tab of your dashboard. The API key goes in the query string, not the request body.
| Method | Path | Auth |
|---|---|---|
| POST | /api/webhook/log/:user_id?key=:api_key | API key in query string |
| Code | Meaning | When it happens |
|---|---|---|
| 200 | OK | Webhook received and logged successfully. |
| 401 | Unauthorized | API key is missing, invalid, or has been deactivated. |
| 404 | Not Found | The user_id in the URL does not match any account. |
| 422 | Unprocessable | Request body is malformed or missing a required field. |
| 429 | Too Many Requests | Rate limit exceeded. The webhook endpoint allows 60 requests per minute per API key. Back off and retry after 60 seconds. |
| 500 | Server Error | Unexpected error on our end. If this persists, contact support. |
Send a JSON body with each webhook call. The API key goes in the URL as a query parameter. Only workflow_name is required; everything else is optional.
POST /api/webhook/log/YOUR_USER_ID?key=YOUR_KEY { "workflow_name": "Send Weekly Report", // required "workflow_id": "zapier-weekly-001", "status": "success", // "success" | "error" — defaults to "success" "duration_ms": 1240, "platform": "zapier", // defaults to "webhook" "error_message": null, "extra_data": [ // optional — add any custom key/value pairs { "key": "records_processed", "value": 42 }, { "key": "triggered_by", "value": "schedule" }, { "key": "environment", "value": "production" } ] }
| Field | Type | Required | Description |
|---|---|---|---|
| workflow_name | string | required | Name of the workflow. Each unique name creates a separate tracked automation. |
| status | string | optional | "success" or "error". Defaults to "success" if omitted. |
| workflow_id | string | optional | A stable ID for the workflow (e.g. a Zapier Zap ID). Auto-generated from workflow_name if omitted. |
| duration_ms | integer | optional | How long the run took in milliseconds. |
| platform | string | optional | The tool the automation runs on: zapier, make, n8n, etc. Defaults to "webhook". |
| error_message | string | optional | Error detail when status is "error". |
| extra_data | array | optional | Array of { "key": string, "value": any } objects. Add as many custom pairs as you need — e.g. records_processed, triggered_by, environment. |
One key, many automations
You use a single API key across all your workflows. Each automation is tracked separately based on its
workflow_name field, so you get per-automation scoring without managing multiple keys.
Every unique workflow_name automatically creates a separate automation entry in your dashboard with its own reliability score, grade, and history.
// Workflow 1 POST /api/webhook/log/YOUR_USER_ID?key=YOUR_KEY { "workflow_name": "Email Welcome Sequence", "status": "success", "duration_ms": 1240, "platform": "zapier" } // Workflow 2 — same key, tracked separately POST /api/webhook/log/YOUR_USER_ID?key=YOUR_KEY { "workflow_name": "Lead Sync to CRM", "status": "error", "duration_ms": 820, "error_message": "Field mapping failed", "platform": "make" }
Tip: Your API key and user ID are both on the Automations tab in your dashboard. The key goes in the URL query string, not the request body.
How quiet workflow detection works: Leaf Loom can only detect a workflow going silent if it has sent at least one webhook previously. A brand-new automation that has never fired will not appear — there is no record of it yet. Once a workflow has run at least once, Leaf Loom tracks its last-seen timestamp and will flag it on your dashboard if no webhook arrives for 7 or more days.
Automated checks on every dashboard load
Every time you open your dashboard, Leaf Loom runs two health checks across your automations and surfaces issues at the top of the page — no configuration needed.
Any automation that Leaf Loom has previously seen but has not received a webhook from in 7 or more days is flagged as quiet. This catches workflows that may have been paused, broken upstream, or accidentally disconnected from the webhook.
Caveat: Only automations that have sent at least one webhook are tracked. A brand-new automation that has never fired will not appear here — Leaf Loom has no record of it yet.
Any automation with a 50% or higher error rate across at least 3 runs in the last 30 days is flagged. This distinguishes a one-off failure from a structural problem — a workflow that keeps failing the same way on every run.
Open the Reports tab and select the flagged automation to see the per-day breakdown and identify when the errors started.
Health alerts are informational only — they do not affect your reliability score or grade. They are designed to surface problems you might otherwise miss by only looking at aggregate metrics.
How scoring builds over time
Score confidence increases as more data is collected. A brand new automation starts ungraded and builds toward a stable score as runs accumulate. The score equals the success rate percentage. Grades are assigned as follows: A ≥ 95%, B ≥ 85%, C ≥ 75%, D ≥ 60%, F < 60%.
The dashboard lets you view each automation across four time windows. Switching periods re-evaluates all scoring rules against only the runs inside that window, so the score, Times Fired counts, and Total Points all update to reflect that specific period.
| Day | Score | Runs | Success Rate | Status |
|---|---|---|---|---|
| Day 0 | — | — | — | No data yet |
| Day 3 | 58 | 12 | 58% | Grade F — calibrating |
| Day 7 | 68 | 34 | 68% | Grade D — pattern emerging |
| Day 15 | 82 | 89 | 82% | Grade C — improving |
| Day 22 | 91 | 156 | 91% | Grade B — solid |
| Day 30 | 95 | 210 | 95% | Grade A — high confidence |
OK
Log recorded. Score updated.
Bad Request
Missing or invalid fields in the request body.
Unauthorized
Invalid or missing API key.
Not Found
User ID not found.
Custom scoring rules let you adjust an automation's reliability score beyond the raw success rate. Rules are evaluated against every individual run in the selected period — not a merged snapshot. Up to 500 active rules are supported per automation.
The base score is the success rate (0–100). Each rule is evaluated against every individual run in the period. The average per-run adjustment is added to the base score. The final result is clamped between 0 and 100.
| Rule | Points per Hit | Times Fired | Total Points | Avg / Run |
|---|---|---|---|---|
| Low Record Count | −3 | 11 / 200 | −33 | −0.17 |
| Full Sync Completed | +2 | 66 / 200 | +132 | +0.66 |
| Sync Warnings Detected | −2 | 67 / 200 | −134 | −0.67 |
| avg_adjustment (Σ Avg/Run) | −0.2 | |||
The score reflects what the average run contributed — not a single worst-case or best-case evaluation.
Every rule condition and formula can reference these variables. Metric fields are always present. Metadata fields appear automatically as new extra_data keys are received via webhook.
| Field | Type | Source | Description |
|---|---|---|---|
| success_rate | float | Metric | Percentage of successful runs (0–100) |
| avg_duration | float | Metric | Average run duration in milliseconds |
| total_runs | float | Metric | Total number of runs recorded |
| duration_ms | float | Metric | Duration of the most recent run in ms |
| <extra_data key> | any | Metadata | Any key sent in the extra_data array of a webhook run. Discovered automatically. |
Each rule has an optional condition and a required action. If no condition is set, the rule fires unconditionally on every evaluation.
{
"name": "Penalise high record count",
"if": {
"key": "records_processed",
"operator": ">",
"value": "100"
},
"action": {
"type": "static_penalty",
"value": 10
}
}| Operator | Works with | Description |
|---|---|---|
| = | text, number | Exact match (case insensitive) |
| != | text, number | Not equal (case insensitive) |
| > | number | Greater than |
| < | number | Less than |
| >= | number | Greater than or equal |
| <= | number | Less than or equal |
| contains | text | Field value contains the given string |
| exists | any | Field is present in the run context |
Subtract a fixed amount from the score.
{ "type": "static_penalty", "value": 10 } Add a fixed amount to the score.
{ "type": "static_bonus", "value": 5 } Compute the adjustment using a mathematical expression over context fields. Only numeric fields can be used. Supports +, -, *, /, % and parentheses.
{ "type": "dynamic_formula", "formula": "(avg_duration - 1000) / 500" } Score clamping: The adjusted score is always kept between 0 and 100. If aggressive bonus rules push the raw result above 100, it is capped at 100.0. If aggressive penalty rules push it below 0, it is floored at 0.0. The dashboard will show the clamped value; the formula bar still shows the full calculation so you can see by how much the score was constrained.
| Limit | Value | Notes |
|---|---|---|
| Active rules per automation | 500 | All active rules are evaluated against every run in the period. Delete unused rules to stay within the limit. |
| Triggered rules shown in report | 20 | Top 20 triggered rules are displayed. The total adjustment and formula are always computed from all rules. |
| Adjusted score range | 0 – 100 | The result is clamped. Bonuses cannot push the score above 100; penalties cannot push it below 0. |
| Formula operators | + − × ÷ % | Only numeric constants and context fields are allowed in dynamic_formula rules. No functions or string literals. |
cURL
curl -X POST "http://localhost:8000/api/webhook/log/YOUR_USER_ID?key=YOUR_KEY" \ -H "Content-Type: application/json" \ -d '{"workflow_name":"My Workflow","status":"success","duration_ms":850}'
JavaScript (fetch)
await fetch('http://localhost:8000/api/webhook/log/YOUR_USER_ID?key=YOUR_KEY', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ workflow_name: 'My Workflow', status: 'success', duration_ms: 850 }) });