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

# App Webhooks

> Receive webhook events scoped to the OAuth permissions your integration app has been granted.

An app webhook is tied to a specific OAuth 2.0 application. Lumin delivers events only for users who have authorized your app, and only for the event types covered by the OAuth scopes they granted.

<Note>
  Only the **Workspace Owner** can configure or update the app webhook URL. App webhooks are only available for **Private (Server)** application types.
</Note>

## How app webhooks differ from account webhooks

|                    | Account webhooks                            | App webhooks                                 |
| ------------------ | ------------------------------------------- | -------------------------------------------- |
| **Scope**          | All events, all users in the workspace      | Events from users who authorized your app    |
| **Filtering**      | None — you receive everything               | Limited to the OAuth scopes the user granted |
| **Signing secret** | Primary API key                             | App signing secret                           |
| **Use case**       | Internal integrations, workspace monitoring | Third-party OAuth integrations               |

## Configure an app webhook

<Steps>
  <Step title="Open Developer settings">
    In Lumin, go to **Settings → Developer settings → Integration apps tab**.
  </Step>

  <Step title="Select your app">
    Click the app you want to configure. If you haven't created one yet, click **Create app**.
  </Step>

  <Step title="Enter your webhook URL">
    Under the **Receive events on behalf of users** section, enter your endpoint URL. The URL must use HTTPS.
  </Step>

  <Step title="Save">
    Click **Save**.
  </Step>
</Steps>

## Events received

Your app webhook receives events based on the OAuth scopes users have granted your app. For example, if a user does not grant the `sign:requests.read` or `sign:requests` scope, your app will not receive signature request events for that user.

For the full list of available event types, see [Supported event types](/tabs/guides/webhooks/overview#supported-event-types).

| Delivery behavior         | Detail                                              |
| ------------------------- | --------------------------------------------------- |
| One request per event     | Each event triggers a separate HTTP POST            |
| Real-time delivery        | Events are sent as they occur                       |
| User-scoped coverage      | Only events from users who authorized your app      |
| Scope-dependent filtering | Events limited to the OAuth scopes the user granted |

## Verify webhook signatures

Every request Lumin sends includes the following headers:

* **`User-Agent`**: Always `Lumin Sign API`
* **`X-Signature`**: An HMAC-SHA256 hex digest of the request body, signed with your **app's signing secret**

<Warning>
  Always verify the `X-Signature` header before processing a webhook payload. Reject any request where the signature does not match.
</Warning>

Your app's signing secret is available in **Settings → Developer settings → Integration apps → Application details**.

**To verify a signature:**

<Steps>
  <Step title="Read the header">
    Extract the `X-Signature` value from the incoming request headers.
  </Step>

  <Step title="Compute the expected signature">
    Compute HMAC-SHA256 of the raw request body using your app's signing secret.
  </Step>

  <Step title="Compare">
    Compare your computed value to the `X-Signature` header. Use a constant-time comparison to avoid timing attacks.
  </Step>

  <Step title="Reject mismatches">
    If the values do not match, reject the request with a non-200 status.
  </Step>
</Steps>

**Example using OpenSSL:**

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
signing_secret='your_app_signing_secret'
json='{"event":{"event_time":1694664207595,"event_type":"signature_request_sent"},"signature_request":{"signature_request_id":"fa5c8a0b0f492d768749333ad6fcc214c111e967","title":"My first request"}}'

echo -n $json | openssl dgst -sha256 -hmac $signing_secret
# Expected X-Signature: 3810cb411041efab279d31698b9584372e5ede9d1641fbb354810f16e51be81c
```

## Respond to events

Your endpoint must return HTTP `200 OK` within 30 seconds of receiving a request. No response body is required.

```http theme={"theme":{"light":"github-light","dark":"github-dark"}}
HTTP/1.1 200 OK
Content-Type: application/json

{}
```

<Tip>
  Acknowledge the request immediately and process the event asynchronously in a background job. This ensures you stay within the 30-second response window.
</Tip>

## Error handling and retries

If your endpoint does not return `200 OK`, Lumin retries the delivery. The following conditions trigger a retry:

* HTTP `4xx` or `5xx` response
* No response within 30 seconds (timeout)
* Connection failure

For the full retry schedule, see [Retry schedule](/tabs/guides/webhooks/overview#retry-schedule).
