> ## Documentation Index
> Fetch the complete documentation index at: https://docs.uselamina.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Handle Long-Running Executions

> Recommended patterns for apps that take minutes to complete, especially image-heavy and video-heavy workflows.

## Why Lamina Uses Async Jobs

Some Lamina apps complete in seconds. Others, especially multi-step image/video pipelines, may take much longer.

Because of that, `POST /v1/apps/{appId}/runs` starts work and returns an execution handle immediately instead of blocking until the final output is ready.

If you are integrating Lamina into a backend, queue system, merchant tool, or content platform, design around the execution lifecycle from the start.

## Two Delivery Patterns

### Option 1: Webhook (Recommended)

Pass a webhook URL as a query parameter. When the execution completes, we POST the results to your URL.

```bash theme={null}
POST /v1/apps/{appId}/runs?webhook=https://your-server.com/callback
```

Benefits:

* No polling loop needed
* Results arrive as soon as they're ready
* Signed with ED25519 for security
* Automatic retries (3 attempts with backoff)

### Option 2: Polling

Poll `GET /v1/runs/{runId}` on an interval until `status` reaches `completed` or `failed`.

Use different polling intervals depending on the workflow:

* Short image jobs: every 3-5 seconds
* Heavier multi-step jobs: every 5-10 seconds
* Long-running video jobs: every 10-15 seconds

Avoid polling every second in production.

## Recommended Backend Flow

<Steps>
  <Step title="Start the execution">
    Call `POST /v1/apps/{appId}/runs?webhook=<your_url>` and store the returned run ID.
  </Step>

  <Step title="Persist the run ID">
    Save it in your job table, queue, or request state so you can resume later.
  </Step>

  <Step title="Receive results">
    Your webhook endpoint receives the completed results, or poll `GET /v1/runs/{runId}` as a fallback.
  </Step>
</Steps>

## What To Persist

For every execution you start, store at least:

* `runId`
* `appId`
* input payload
* current status
* started timestamp

This makes retries, dashboards, and support much easier.

## Failure Handling

If an execution fails:

* inspect the top-level `errorMessage`
* inspect each output's `error`
* keep the original inputs for debugging or replay

If your product has end users, show a user-friendly status while keeping the raw error for logs and support tooling.

## Webhook Verification

When receiving webhook callbacks, verify the signature to ensure it's from Lamina:

1. Fetch the public key from `GET /v1/webhooks/signing-key`
2. Verify the ED25519 signature: `verify(signature, "<timestamp>.<body>", publicKey)`
3. Reject timestamps older than 5 minutes (replay protection)

See the [Webhook Signing Key](/api-reference/get-webhook-signing-key) reference for verification code examples.
