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

# Send webhook events to Azure Blob Storage

> Configure the Azure Blob Storage webhook connector to store selected Novu events as batched blobs.

export const connectorName_0 = "Azure Blob Storage"

The Azure Blob Storage connector writes selected Novu webhook events to a container. Each delivered batch creates a new blob.

Novu webhook delivery is powered by [Svix](https://docs.svix.com/introduction). This connector maps to the Svix [object storage advanced endpoint](https://docs.svix.com/advanced-endpoints/object-storage), which is shared with Amazon S3 and Google Cloud Storage.

<Note>
  Outbound webhooks are available on [Team and Enterprise plans](https://novu.co/pricing).
</Note>

## Prerequisites

* An Azure storage account
* An existing blob container, created before you configure the endpoint
* The storage account name and one of its access keys

### Access model

The connector authenticates with a storage account access key. An account key grants full access to every container in that storage account, and it cannot be narrowed to a single container the way an Azure RBAC role assignment or a container-scoped SAS token can.

Because of that, use a storage account dedicated to webhook exports rather than one that also holds application data, and rotate the key on your normal schedule. Azure supports two keys per account so you can rotate without downtime: update the endpoint to the second key, then regenerate the first.

## Configuration

| Dashboard field    | Required | Description                                             |
| ------------------ | -------- | ------------------------------------------------------- |
| **Container**      | Yes      | Name of the destination Azure Blob Storage container.   |
| **Account**        | Yes      | Azure storage account name.                             |
| **Access key**     | Yes      | Access key for the storage account.                     |
| **Transformation** | Yes      | JavaScript that defines the blob key, format, and data. |

## Transformation contract

Object storage connectors run the handler once per delivery batch.

The handler receives one delivery batch:

* `input.events`, an array whose length is capped by the endpoint batch size
* `input.events[].eventType`, the Novu event type, for example `message.sent`
* `input.events[].payload`, the webhook body

The webhook body wraps the resource in an envelope. `payload.object` is the resource type as a string, such as `"message"`, and the resource itself is under `payload.data.object`. For message events, fields such as `subscriberId` and `channel` are therefore at `event.payload.data.object`. Other event families use different resource shapes, so guard extracted fields with defaults.

The handler must return:

```ts theme={null}
{
  config: {
    format: "jsonl" | "json" | "raw";
    key: string;
  };
  data: unknown[] | string;
}
```

| Return field    | Behavior                                                                                                                   |
| --------------- | -------------------------------------------------------------------------------------------------------------------------- |
| `config.format` | `"jsonl"`, `"json"`, or `"raw"`. Defaults to `"jsonl"`.                                                                    |
| `config.key`    | Object name for this batch. A timestamp is appended after the transformation runs, so each batch produces a unique object. |
| `data`          | The batch contents. Use an array for `"jsonl"` and `"json"`. Use a string for `"raw"`.                                     |

The default template returns the batch unchanged:

```js theme={null}
function handler(input) {
  return {
    config: {
      format: "jsonl",
      key: "object-generated-by-svix",
    },
    data: input.events,
  };
}
```

With `"jsonl"`, each line is one event:

```jsonl theme={null}
{"payload":{"email":"joe@enterprise.io"},"eventType":"user.created"}
{"payload":{"id":12,"timestamp":"2025-07-21T14:23:17.861Z"},"eventType":"user.login"}
```

### Novu example

This example groups objects by date and keeps each payload intact:

```js theme={null}
function handler(input) {
  const partition = new Date().toISOString().slice(0, 10);

  const data = input.events.map((event) => ({
    eventType: event.eventType,
    payload: event.payload,
  }));

  return {
    config: {
      format: "jsonl",
      key: `novu-events/dt=${partition}/events`,
    },
    data,
  };
}
```

The connector appends a timestamp to `config.key`, so use the key for the prefix structure rather than adding a uniqueness suffix.

### Batching behavior

One delivered batch produces one object. Treat its contents as a variable-length batch and read it line by line when using `"jsonl"`.

## Configure in the Dashboard

<Steps>
  <Step>
    ## Prepare the container

    Create the storage account and container. Keep the account name and access key available.
  </Step>

  <Step>
    ## Add the endpoint

    Open **[Webhooks](https://dashboard.novu.co/webhooks)** in the Novu Dashboard, select **Endpoints**, click **Add Endpoint**, and choose **{connectorName_0}**.
  </Step>

  <Step>
    ## Enter connection details

    Enter **Container**, **Account**, and **Access key**.
  </Step>

  <Step>
    ## Configure the transformation

    Start from the provided object storage template. Change `config.key` to the prefix you want and keep `config.format` aligned with `data`.
  </Step>

  <Step>
    ## Select event types

    Choose only the [event types](/platform/developer/webhooks/event-types) that you want to store. The connector processes selected events in batches.
  </Step>

  <Step>
    ## Test the endpoint

    Create the endpoint. Open its **Testing** tab, choose a subscribed event type, and click **Send Example**. Confirm success in **Logs**, then verify that a timestamp-suffixed blob exists in the container and contains the expected data.
  </Step>
</Steps>

## Verify delivery

List the most recent blobs under your key prefix:

```bash theme={null}
az storage blob list \
  --account-name mystorageaccount \
  --container-name novu-events \
  --prefix novu-events/ \
  --query "[].name" \
  --output tsv
```

Download one blob and compare its contents with the transformation output in the endpoint's **Logs**:

```bash theme={null}
az storage blob download \
  --account-name mystorageaccount \
  --container-name novu-events \
  --name "novu-events/dt=2026-09-01/events-<timestamp>" \
  --file ./events.jsonl
```

## Troubleshooting

* **Authentication fails**: Recheck the account name and access key. Confirm the key is active and can access the configured container.
* **The container is not found**: Confirm the container already exists in the specified storage account and that its spelling and casing match.
* **A successful delivery has unexpected contents**: Inspect the transformed output in **Logs** and verify that `format` and `data` agree.
* **A raw blob is invalid**: Return a string in `data` when `config.format` is `"raw"`.
* **Expected events are absent**: Check the endpoint's selected event types and the current Novu environment.

## Related

* [Webhook connectors overview](/platform/developer/webhooks/connectors)
* [Webhook event types](/platform/developer/webhooks/event-types)
* [Webhook delivery, retries, and recovery](/platform/developer/webhooks/webhooks#recovering-and-resending-failed-messages)

## Official references

* [Svix object storage endpoints](https://docs.svix.com/advanced-endpoints/object-storage)
* [Svix advanced endpoint types](https://docs.svix.com/advanced-endpoints)
* [Azure Blob Storage documentation](https://learn.microsoft.com/azure/storage/blobs/)
