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

# Webhook Events

This type of event subscribes to a webhook and then receives event notifications.

## Methods

The following methods can be implemented for webhook events:

### Subscribe

* **Method key:** `subscribe`
* **Supported implementation types:** `javascript`, `rest-api-mapping`, `graphql-api-mapping`

This function subscribes to events in the external app.

**Arguments:**

* `webhookUri` – string, URI webhooks should be sent to. It should be passed to the external app.
* `parameters` – object, parameters (matching `parametersSchema`) of this particular event.

**Result:**

* `state` – object, subscription state. This state will be saved and passed as `state` to `unsubscribe` and `handle` functions.
* `nextRefreshTimestamp` – number, optional, timestamp in milliseconds when the next refresh should be performed. Used to refresh the subscription if the external app requires it.

Example:

```javascript theme={null}
// Example: Subscribe to a webhook and return the webhookId.
export default async function subscribe({ apiClient, webhookUri, parameters }) {
  const body = { webhookUri, '<some_key>': '<some_value>' }
  const response = await apiClient.post(parameters.path, body)

  return {
    state: {
      webhookId: response?.id,
    },
  }
}
```

### Handle

* **Method key:** `handle`
* **Supported implementation types:** `javascript`, `mapping`

This function processes notification requests from the external app.\
If the external app requires a response, you can specify parameters (`headers`, `data`) for the response in the `response` property (optional).
This can be useful if the external API requires confirmation of the webhook URI.

**Arguments:**

* `query` – object, webhook request query received from the external app
* `data` – object, webhook request body received from the external app (parsed based on Content-Type)
* `headers` – object, webhook request headers received from the external app
* `state` – object, state returned from `subscribe` function
* `parameters` – object, parameters (matching `parametersSchema`) of this particular event
* `rawBody` – string, raw unparsed body of the webhook request (useful for signature verification or when you need access to the original request body)

**Result:**

* `events` – list of objects, list of events extracted from the webhook. Each `event` is an object with `type` and `record` fields.
* `response` – object, optional HTTP response with properties:
  * `headers` – object
  * `status` – number
  * `data` – HTTP body (based on Content-Type)
* `state` – a new value of the state if it needs to be updated.

Example result:

```json theme={null}
{
  "events": [
    {
      "record": {
        "id": "1"
        // ...other fields that match collection fieldsSchema
      }
    }
  ],
  "response": {
    "headers": {
      "x-response-header": "header value"
    },
    "data": {
      "responseField": "value"
    }
  }
}
```

Example:

```javascript theme={null}
// Example: Handle a webhook and return events.
export default async function handle({ body, query, parameters, rawBody }) {
  // Verify webhook signature if needed
  if (query.signatureRequest) {
    const signature = createHmac('sha256', parameters.webhookSecret).update(rawBody).digest('hex')
    if (signature !== headers['x-webhook-signature']) {
      throw new Error('Invalid webhook signature')
    }
  }

  // Process the webhook payload
  return {
    events: [
      {
        type: 'created', // Possible values: 'created', 'updated', 'deleted'
        record: body.record,
      },
    ],
    response: {
      headers: {
        'Content-Type': 'text/plain',
      },
      data: query?.data,
    },
  }
}
```

### Unsubscribe

* **Method key:** `unsubscribe`
* **Supported implementation types:** `javascript`, `rest-api-mapping`, `graphql-api-mapping`

This function should send a request to delete the webhook by `webhookId` received in the `subscribe` method.

**Arguments:**

* `state` – state returned from `subscribe`
* `parameters` – object, parameters (matching `parametersSchema`) of this particular event

Example:

```javascript theme={null}
// Example: Unsubscribe from a webhook.
export default async function unsubscribe({ apiClient, state, parameters }) {
  await apiClient.delete(`${parameters.path}/${state.webhookId}`)
}
```

### Refresh (optional)

* **Method key:** `refresh`
* **Supported implementation types:** `javascript`, `rest-api-mapping`, `graphql-api-mapping`

Some applications require refreshing the subscription to keep it active.\
This method should be implemented if the `nextRefreshTimestamp` is returned from the `subscribe` method.

**Arguments:**

* `state` – state returned from `subscribe`
* `webhookUri` – string, URI webhooks should be sent to
* `parameters` – object, parameters (matching `parametersSchema`) of this particular event

**Response:**

* `state` – updated state information if it needs to be updated
* `nextRefreshTimestamp` – number, timestamp in milliseconds when the next refresh should be performed

Example:

```javascript theme={null}
// Example: Refresh a webhook subscription.
export default async function refresh({ apiClient, state, parameters }) {
  await apiClient.post(`${parameters.path}/${state.webhookId}/refresh`)
  return {
    nextRefreshTimestamp: Date.now() + 1000 * 60 * 60 * 24, // Refresh in 24 hours
  }
}
```
