> ## 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 Google Cloud Storage

> Configure the Google Cloud Storage webhook connector to store selected Novu events as batched objects.

export const connectorName_0 = "Google Cloud Storage"

The Google Cloud Storage connector writes selected Novu webhook events to a bucket. Each delivered batch creates a new object.

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 Azure Blob Storage.

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

## Prerequisites

* An existing Google Cloud Storage bucket, created before you configure the endpoint
* A Google Cloud service account with a JSON key that can create objects in that bucket

### Required permission

Writing a new object requires the `storage.objects.create` permission, which is included in the predefined `roles/storage.objectCreator` role. That role can create objects but cannot read, overwrite, or delete them, which is usually what an append-only event archive wants. Grant it on the bucket rather than the project:

```bash theme={null}
gcloud storage buckets add-iam-policy-binding gs://novu-events \
  --member="serviceAccount:novu-writer@my-gcp-project.iam.gserviceaccount.com" \
  --role="roles/storage.objectCreator"
```

See [Cloud Storage IAM roles](https://cloud.google.com/storage/docs/access-control/iam-roles) for the full permission reference. Paste the complete JSON key into the credentials field and treat it as a secret.

## Configuration

| Dashboard field    | Required | Description                                                          |
| ------------------ | -------- | -------------------------------------------------------------------- |
| **Bucket**         | Yes      | Name of the destination Google Cloud Storage bucket.                 |
| **Credentials**    | Yes      | Google Cloud service account credentials JSON, supplied as a string. |
| **Transformation** | Yes      | JavaScript that defines the object 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 bucket

    Create the bucket and service account credentials. Keep the complete credentials JSON 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 **Bucket** and the service account **Credentials** JSON.
  </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

    Select the [event types](/platform/developer/webhooks/event-types) to store. Selecting fewer event types also limits which events reach the transformation.
  </Step>

  <Step>
    ## Test the endpoint

    Create the endpoint. In **Testing**, send an example for a subscribed event type. Confirm success in **Logs**, then verify the bucket contains a new timestamp-suffixed object with the expected content.
  </Step>
</Steps>

## Verify delivery

List objects under your key prefix, then read one and compare it with the transformation output in the endpoint's **Logs**:

```bash theme={null}
gcloud storage ls "gs://novu-events/novu-events/**"

gcloud storage cat "gs://novu-events/novu-events/dt=2026-09-01/events-<timestamp>"
```

If `roles/storage.objectCreator` is the only role you granted, the connector's service account cannot list or read objects. Run these commands with your own credentials rather than the connector's key.

## Troubleshooting

* **Credentials are rejected**: Paste the complete service account credentials JSON and confirm it is valid JSON.
* **The bucket cannot be accessed**: Confirm the bucket exists and the supplied service account can write to it.
* **No object appears after a successful test**: Check the exact bucket name and inspect the transformed output in **Logs**.
* **Object contents are malformed**: Confirm `config.format` matches `data`. For `"raw"`, return a string.
* **Events are missing**: Confirm the endpoint subscribes to those event types in the intended 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)
* [Cloud Storage IAM roles](https://cloud.google.com/storage/docs/access-control/iam-roles)
