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

# Authorization

> Secure your Lettr webhook endpoints with none, basic, or OAuth 2.0 authentication so only Lettr can deliver events to your server.

Securing your webhook endpoints ensures that only Lettr can send events to your server. Lettr supports multiple authentication methods for outbound webhook requests, which you configure when creating a webhook in the dashboard.

## Authentication Types

When creating or editing a webhook in the dashboard, you choose an authentication type that Lettr uses when making requests to your endpoint.

| Auth Type | Description                                                                          |
| --------- | ------------------------------------------------------------------------------------ |
| `none`    | No authentication. Lettr sends requests without credentials.                         |
| `basic`   | HTTP Basic Authentication. Lettr includes a username and password in each request.   |
| `oauth2`  | OAuth 2.0 Client Credentials. Lettr obtains a token and includes it in each request. |

## Basic Authentication

With basic auth, Lettr includes an `Authorization: Basic ...` header in every webhook request. You provide the username and password when creating the webhook in the dashboard.

### Verifying Basic Auth

Verify the credentials in your webhook handler:

```javascript theme={null}
import express from 'express';

const app = express();

app.post('/webhooks/lettr', (req, res, next) => {
  const authHeader = req.headers.authorization;

  if (!authHeader || !authHeader.startsWith('Basic ')) {
    return res.sendStatus(401);
  }

  const credentials = Buffer.from(authHeader.slice(6), 'base64').toString();
  const [username, password] = credentials.split(':');

  if (username !== process.env.WEBHOOK_USERNAME || password !== process.env.WEBHOOK_PASSWORD) {
    return res.sendStatus(401);
  }

  next();
}, express.json(), (req, res) => {
  // Credentials verified - process the webhook
  res.sendStatus(200);

  setImmediate(() => {
    processEvents(req.body).catch(console.error);
  });
});
```

### PHP (Laravel)

```php theme={null}
Route::post('/webhooks/lettr', function (Request $request) {
    $username = $request->getUser();
    $password = $request->getPassword();

    if ($username !== config('services.lettr.webhook_username')
        || $password !== config('services.lettr.webhook_password')) {
        return response('Unauthorized', 401);
    }

    // Credentials verified - process the webhook
    $events = $request->all();
    dispatch(new ProcessWebhookEvents($events));

    return response('OK', 200);
});
```

### Python (Flask)

```python theme={null}
from flask import Flask, request, abort

app = Flask(__name__)

@app.route('/webhooks/lettr', methods=['POST'])
def webhook():
    auth = request.authorization

    if not auth or auth.username != os.environ['WEBHOOK_USERNAME'] \
       or auth.password != os.environ['WEBHOOK_PASSWORD']:
        abort(401)

    events = request.get_json()
    process_events(events)
    return 'OK', 200
```

<Tip>
  Store your webhook credentials in environment variables or a secrets manager. Never hardcode them in your source code.
</Tip>

## OAuth 2.0 Authentication

With OAuth 2.0 client credentials, Lettr obtains an access token from your OAuth server before each webhook delivery and includes it as a Bearer token. You provide the client ID, client secret, and token URL when creating the webhook in the dashboard.

### Verifying OAuth Tokens

Verify the Bearer token in your webhook handler:

```javascript theme={null}
import express from 'express';

const app = express();

app.post('/webhooks/lettr', async (req, res, next) => {
  const authHeader = req.headers.authorization;

  if (!authHeader || !authHeader.startsWith('Bearer ')) {
    return res.sendStatus(401);
  }

  const token = authHeader.slice(7);

  try {
    // Validate the token with your OAuth server
    const isValid = await validateOAuthToken(token);
    if (!isValid) {
      return res.sendStatus(401);
    }
    next();
  } catch (err) {
    console.error('Token validation failed:', err);
    return res.sendStatus(401);
  }
}, express.json(), (req, res) => {
  res.sendStatus(200);

  setImmediate(() => {
    processEvents(req.body).catch(console.error);
  });
});
```

## No Authentication

If you choose `none`, Lettr sends webhook requests without any authentication credentials. This is the simplest option but offers no protection against unauthorized requests.

<Warning>
  Using no authentication means anyone who discovers your webhook URL can send fake events to your endpoint. If you choose this option, consider adding other security measures like IP allowlisting.
</Warning>

## Additional Security Measures

### IP Allowlisting

For additional security, restrict your webhook endpoint to only accept requests from Lettr's IP addresses. Contact support for the current list.

```javascript theme={null}
const ALLOWED_IPS = ['203.0.113.10', '203.0.113.11', '203.0.113.12'];

app.post('/webhooks/lettr', (req, res, next) => {
  const clientIp = req.ip || req.connection.remoteAddress;

  if (!ALLOWED_IPS.includes(clientIp)) {
    console.warn(`Webhook request from unauthorized IP: ${clientIp}`);
    return res.sendStatus(403);
  }

  next();
});
```

<Note>
  If you're behind a proxy or load balancer, make sure to configure your application to trust the proxy and extract the real client IP from the `X-Forwarded-For` header.
</Note>

### HTTPS

Always use HTTPS endpoints for your webhooks. This ensures that authentication credentials and event data are encrypted in transit.

### Secret URL Paths

As an additional layer of defense, you can use a hard-to-guess URL path for your webhook endpoint:

```
https://example.com/webhooks/lettr/a1b2c3d4e5f6
```

This is not a substitute for proper authentication, but it adds an extra barrier for anyone scanning for webhook endpoints.

## Checking Webhook Auth Status via API

You can check whether a webhook has authentication configured using the read-only API:

```bash theme={null}
curl -X GET "https://app.lettr.com/api/webhooks/{webhookId}" \
  -H "Authorization: Bearer lttr_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
```

The response includes the auth type and whether credentials are configured:

```json theme={null}
{
  "message": "Webhook retrieved successfully.",
  "data": {
    "id": "webhook-abc123",
    "name": "Order Notifications",
    "url": "https://example.com/webhook",
    "enabled": true,
    "event_types": ["message.delivery", "message.bounce"],
    "auth_type": "basic",
    "has_auth_credentials": true,
    "last_successful_at": "2024-01-15T10:30:00+00:00",
    "last_failure_at": null,
    "last_status": "success"
  }
}
```

<Note>
  The API response shows `auth_type` and `has_auth_credentials` but never exposes the actual credentials. To update authentication settings, use the Lettr dashboard.
</Note>

## Related Topics

<CardGroup cols={2}>
  <Card title="Handling Webhooks" icon="code" href="/learn/webhooks/handling">
    Best practices for processing webhooks reliably
  </Card>

  <Card title="Retries" icon="rotate" href="/learn/webhooks/retries">
    Understand retry behavior and failure handling
  </Card>

  <Card title="Testing" icon="flask" href="/learn/webhooks/testing">
    Test webhooks in development and production
  </Card>

  <Card title="Event Types" icon="list" href="/learn/webhooks/event-types">
    Complete reference of all webhook events
  </Card>
</CardGroup>
