---
title: Delivery verification
description: Authenticate signed Stormedo deliveries and verify that the request body was not changed in transit.
---

Every Stormedo delivery attempt carries a project-scoped Ed25519 delivery token. The token lets a receiver authenticate Stormedo and bind the delivery to the expected project, URL, HTTP method, stable request ID, time window, and body digest.

## Delivery headers

Stormedo controls two headers on every attempt:

```http
Stormedo-Request-Id: req_33uZSQsVaf8aZjDuzkuAq
Stormedo-Signature: compact-EdDSA-JWT
```

Reject missing or duplicated values before calling the SDK verifier.

## Configure a JavaScript verifier

Create one verifier with the project ID and reuse it.

```ts
import { DeliveryVerifier } from "@stormedo/sdk";

const projectId = process.env.STORMEDO_PROJECT_ID!;

const verifier = new DeliveryVerifier(projectId);
```

## Authenticate without checking the body in JavaScript

Use `authenticateDelivery` only when the runtime cannot provide the raw body. This authenticates Stormedo and the request context, but it does not prove payload integrity.

```ts
const delivery = await verifier.authenticateDelivery({
  signature,
  requestId,
  expectedUrl: "https://your-app.example/webhooks/stormedo",
  method: "POST",
});

delivery.payloadIntegrity; // "unchecked"
```

## Verify the exact body in JavaScript

Capture the raw bytes before a JSON or form parser changes them.

```ts
const delivery = await verifier.verifyDelivery({
  signature,
  requestId,
  expectedUrl: "https://your-app.example/webhooks/stormedo",
  method: "POST",
  rawBody,
});

delivery.payloadIntegrity; // "verified"
```

## Configure a Python verifier

Create one verifier with the project ID and reuse it.

```python
import os

from stormedo import DeliveryVerifier

project_id = os.environ["STORMEDO_PROJECT_ID"]

verifier = DeliveryVerifier(project_id)
```

For asynchronous applications:

```python
from stormedo import AsyncDeliveryVerifier

async_verifier = AsyncDeliveryVerifier(project_id)
```

## Authenticate without checking the body in Python

```python
delivery = verifier.authenticate_delivery(
    signature=signature,
    request_id=request_id,
    expected_url="https://your-app.example/webhooks/stormedo",
    method="POST",
)

assert delivery.payload_integrity == "unchecked"
```

## Verify the exact body in Python

```python
delivery = verifier.verify_delivery(
    signature=signature,
    request_id=request_id,
    expected_url="https://your-app.example/webhooks/stormedo",
    method="POST",
    raw_body=raw_body,
)

assert delivery.payload_integrity == "verified"
```

## Handle verification errors

Use `DeliveryVerificationError.code` to handle verification failures.

```ts
import { DeliveryVerificationError } from "@stormedo/sdk";

try {
  await verifier.verifyDelivery({
    signature,
    requestId,
    expectedUrl: "https://your-app.example/webhooks/stormedo",
    method: "POST",
    rawBody,
  });
} catch (error) {
  if (error instanceof DeliveryVerificationError) {
    console.error(error.code);
  }
}
```

```python
from stormedo import DeliveryVerificationError

try:
    verifier.verify_delivery(
        signature=signature,
        request_id=request_id,
        expected_url="https://your-app.example/webhooks/stormedo",
        method="POST",
        raw_body=raw_body,
    )
except DeliveryVerificationError as error:
    print(error.code)
```

## Receiver responsibilities

- Build the complete public destination URL from trusted configuration.
- Do not trust forwarded host or protocol headers without an explicit proxy trust boundary.
- Authenticate or verify while the delivery token is current.
- Store the validated request ID before, or atomically with, the business side effect.
- Parse the application body only after verification when payload integrity is required.

Verification proves who sent the request and what it was bound to. Duplicate-safe business processing is covered separately in the advanced [Idempotency](/docs/concepts/idempotency/) guide.
