> ## Knowledge Base Index
> Fetch the complete knowledge base index at: https://help.lido.app/sitemap.xml
> Use this file to discover available pages before exploring further.
> Pure-Markdown content can be obtained by appending a '.md' suffix to the content URLs listed in the sitemap (without the trailing slash).

# Webhooks and async processing

This article covers two related patterns:

1. **Lido calling your system** when something happens (workflow-side webhooks via the Send Webhook node).
2. **Your system being called by Lido** when extraction completes (the inverse of polling — a workflow listens with a Webhook Trigger).

If you're integrating Lido with your own backend, webhooks usually replace polling.

---

## When to use webhooks

| Pattern | Use case |
| --- | --- |
| **Send Webhook** (Lido → your system) | Notify your app when a workflow finishes; push extracted data to a backend; replace polling with push |
| **Webhook Trigger** (your system → Lido) | Kick off a workflow from any external system that can make an HTTP request |
| **Polling** (your code calls `GET /job-result/...`) | Fine for batch jobs, prototypes, lower-volume systems where you control the timing |

Polling is simpler. Webhooks are more efficient at scale and remove the result-expiration concern (you store the result on receipt, not on poll).

---

## Pattern 1: Lido pushes results to you (Send Webhook)

This is the recommended high-volume pattern. The full pipeline runs *inside* a Lido workflow, and the final node POSTs the extracted data to your backend.

### Workflow shape

```
Trigger (Drive / OneDrive / Lido Mailbox / etc.)
   ↓
Data Extractor
   ↓
Send Webhook → POST https://your-backend.com/lido-callback
```

The Send Webhook node sends a JSON payload of your choosing. Typical:

```json
{
  "source_file": "{{$item.data.file.name}}",
  "extracted": {{$item.previous.data}},
  "workflow_run_id": "{{$run.id}}",
  "timestamp": "{{$now()}}"
}
```

Your backend receives this, stores it, and acks with a 200. If it returns 4xx/5xx, the workflow node's error output fires (see "Verification and retries" below).

### Why this is better than polling

- **No result-expiration issue.** You store the data on receipt, not within 24 hours.
- **No wasted API quota.** No `GET /job-result/...` calls.
- **Faster end-to-end.** You learn about the result the moment it's ready.
- **No state on your side.** No "have I polled this jobId yet?" tracking.

### Authentication

The Send Webhook node has fields for custom headers. Use them to authenticate the inbound call to your backend:

| Header | Use |
| --- | --- |
| `Authorization: Bearer <your-secret>` | Static secret you check on every inbound request |
| `X-Lido-Signature: <hmac>` | If you sign the payload (see "Verification" below) |
| `X-Workflow-Id: <id>` | So you know which workflow sent the request |

Store the secret in a Lido workspace setting and reference it: `{{$workspace.lidoCallbackSecret}}`. Don't paste it inline.

### Verification and retries

Build your endpoint to be idempotent. A request that fails (network blip) should be safe to retry. Two common patterns:

- **Idempotency key.** Include `{{$run.id}}` in the payload; ignore duplicates on your side.
- **Lido-side retry.** The Send Webhook node has retry settings. Configure 3 retries with exponential backoff. After all retries fail, route to an Error / Send Email node so you find out about the dead letter.

---

## Pattern 2: Your system kicks off a workflow (Webhook Trigger)

The inverse: your application has a "Process invoice" button that POSTs to a Lido workflow URL.

### Workflow shape

```
Webhook Trigger (Lido-hosted URL: https://hooks.lido.app/{token})
   ↓
Data Extractor (Source File: {{$item.data.file_url}})
   ↓
... rest of pipeline ...
```

### Setting it up

1. In a workflow, add **Webhook Trigger** as the first node.
2. Lido generates a unique URL.
3. Copy the URL. Send POST requests to it from your system; the body becomes `{{$item.data.*}}` for downstream nodes.
4. Optionally configure expected headers (e.g., `Authorization`) so the workflow rejects unauthenticated calls.

### Example

```python
import requests

response = requests.post(
    "https://hooks.lido.app/abc123xyz",
    json={
        "file_url": "https://your-cdn.com/invoices/abc.pdf",
        "vendor_id": 42,
        "submitted_by": "aleks@example.com"
    },
    headers={"Authorization": "Bearer your-shared-secret"}
)
```

The workflow fires immediately. Lido responds with 202 Accepted (the workflow runs asynchronously) or, if you've configured a synchronous response, 200 with the workflow's final output.

### Synchronous vs. asynchronous

- **Asynchronous (default):** Lido returns 202 immediately. Your system kicks off and forgets. Use the inverse Send Webhook pattern if you need the result back.
- **Synchronous:** the workflow runs to completion before responding. Your call blocks until the result is ready. Configure this in the Webhook Trigger node settings; useful for short workflows where the caller wants the result inline.

Synchronous mode is bound by your client's HTTP timeout. Lido extraction takes 10–30+ seconds, so synchronous works only when your client tolerates that wait.

---

## Combining both: end-to-end async

Most production integrations combine both:

```
Your app → POST hooks.lido.app/abc123 (Webhook Trigger)
            ↓
       Data Extractor
            ↓
       Send Webhook → POST your-backend.com/lido-callback
            ↓
       (your backend stores the result and notifies the user)
```

This is fully async. Your app submits, gets 202, and forgets. Minutes later, your backend gets a callback with the extracted data.

---

## Tips

- **Always use HTTPS** for both directions.
- **Authenticate with a shared secret** at minimum. Validate on your side; reject anything missing the header.
- **Make endpoints idempotent.** Retries happen.
- **Log workflow run IDs** on both sides so you can match Lido-side runs to your-side records.
- **Set up dead-letter alerting.** If Send Webhook exhausts its retries, route to a "manual review" channel — don't silently drop.
- **Use the Webhook Trigger's "test" feature** in the workflow editor to see the exact payload your system is sending.

---

## Common mistakes

- **Plain HTTP endpoints.** Use HTTPS. Always.
- **No auth on Webhook Trigger.** Anyone with the URL can fire your workflow. Add a header check.
- **No retry budget on Send Webhook.** A blip on your side and the data is lost.
- **Synchronous when async is appropriate.** Holding an HTTP connection open for 30 seconds will cause timeouts at every load balancer in between.
- **Returning 4xx for transient errors.** A 4xx tells Lido "don't retry — this will never work." Use 5xx for transient.
- **Forgetting that workflow trigger payloads count toward page allowance** if they include file processing. Plan capacity accordingly.

---

## Related articles

- Lido API: quickstart and authentication
- Extract data via the API: deep dive
- Triggers: how workflows start
- Build your first workflow
- Error handling and retries