# Get Web Hooks

> GET /v1/hooks/web · Apps platform API

Source: https://developer.kustomer.com/api/apps-platform/get-web-hooks

```http
GET https://{orgname}.api.kustomerapp.com/v1/hooks/web
Authorization: Bearer <API key>
```

## Authorization

Requires any one of these roles: `org.permission.hook.read`; or legacy `org.admin.hook.read`. The API key or user must hold at least one of them. Legacy roles predate permission sets and still work.

Fetches web hooks.

A web hook allows you to pass data into Kustomer by posting to a designated endpoint.

The webhook contains a URL to post data to and a workflow event to process the incoming data.

This endpoint will return a maximum of 10 pages with 99 entries.

## About Inbound hooks

An inbound hook is an address that Kustomer hosts for you, either a URL or an email address. Whatever you send to it becomes an event that a workflow can use as its trigger, to create or update customers, conversations or custom objects.

Hooks bring data into Kustomer. They are the opposite of [outbound webhooks](https://developer.kustomer.com/api/apps-platform#outbound-web-hooks), where Kustomer sends its events to your URL. You create a hook in **Settings › Platform › Inbound webhooks**, with these endpoints, or from the `hooks` section of an [app definition](https://developer.kustomer.com/docs/apps/workflows-actions-triggers#hooks-get-data-in). Kustomer returns a unique address (`url`, or `email` for email hooks) and an `eventName` such as `kustomer.hooks.web.<id>.receive`. A hook doesn't change any data by itself: each request it receives becomes a transaction and fires that event, and a [workflow](https://developer.kustomer.com/docs/workflows) with the hook as its trigger decides what to create or update.

#### Three kinds of hook

| | Form hook | Web hook | Email hook |
| --- | --- | --- | --- |
| Address | `POST /v1/hooks/form/{org}/{hash}` | `POST /v1/hooks/web/{hash}` | An email address you add as Bcc |
| Authentication | None | An API key with the `org.hooks` role, as `Authorization: Bearer` | None |
| Accepts | JSON, form data (including files) and XML | JSON, form data (including files) and XML | HTML email |
| Best for | Public web forms | Server-to-server integrations where you need to trust the sender | Order and shipping emails you already send |

Form hooks also answer at the older `/v1/hooks/form/{hash}`, but the `{org}/{hash}` form is preferred and is the only one that supports reCAPTCHA. Anyone who has a form hook or email hook address can send to it, so validate the data in the workflow. Request bodies are limited to 400 KB; larger ones fail with `413`.

For email hooks, Kustomer parses the sender, recipients, subject, bodies and attachments. It also extracts structured JSON from a `<script type="application/ld+json">` tag in the HTML, but only when the tag has a `data-kustomer-key` attribute equal to the hook's `key`.

#### Transactions

Every request a hook accepts is stored as a transaction, and the response contains its ID. The transaction is what the workflow trigger receives: your payload is under `attributes.data`, so workflow steps refer to fields as `/#steps.1.attributes.data.<field>`.

The transactions endpoints are a debugging aid, not a log of everything received. They only return transactions that arrived while the hook's `debug` flag was on (set it with `PATCH`), and each one stays visible for 24 hours.

#### Which endpoint to use

| To | Call |
| --- | --- |
| Create a hook | [`POST /v1/hooks/form`](https://developer.kustomer.com/api/apps-platform/create-form-hook), [`POST /v1/hooks/web`](https://developer.kustomer.com/api/apps-platform/create-web-hook) or [`POST /v1/hooks/email`](https://developer.kustomer.com/api/apps-platform/create-email-hook) |
| List hooks of one kind | [`GET /v1/hooks/form`](https://developer.kustomer.com/api/apps-platform/get-form-hooks), [`GET /v1/hooks/web`](https://developer.kustomer.com/api/apps-platform/get-web-hooks) or [`GET /v1/hooks/email`](https://developer.kustomer.com/api/apps-platform/get-email-hooks) |
| Read one hook, including its address | [`GET /v1/hooks/form/{id}`](https://developer.kustomer.com/api/apps-platform/get-a-form-hook-by-id), [`GET /v1/hooks/web/{id}`](https://developer.kustomer.com/api/apps-platform/get-a-web-hook-by-id) or [`GET /v1/hooks/email/{id}`](https://developer.kustomer.com/api/apps-platform/get-an-email-hook-by-id) |
| Rename a hook, or turn debugging on or off | [`PATCH /v1/hooks/form/{id}`](https://developer.kustomer.com/api/apps-platform/update-form-hook), [`PATCH /v1/hooks/web/{id}`](https://developer.kustomer.com/api/apps-platform/update-web-hook) or [`PATCH /v1/hooks/email/{id}`](https://developer.kustomer.com/api/apps-platform/update-email-hook) |
| Send data to a form hook | [`POST /v1/hooks/form/{org}/{hash}`](https://developer.kustomer.com/api/apps-platform/create-form-hook-org-transaction), or the older [`POST /v1/hooks/form/{hash}`](https://developer.kustomer.com/api/apps-platform/create-form-hook-transaction) |
| Send data to a web hook | [`POST /v1/hooks/web/{hash}`](https://developer.kustomer.com/api/apps-platform/create-web-hook-transaction) |
| See what a hook received while debugging | [`GET /v1/hooks/form/{id}/transactions`](https://developer.kustomer.com/api/apps-platform/get-form-hook-transactions), [`GET /v1/hooks/web/{id}/transactions`](https://developer.kustomer.com/api/apps-platform/get-web-hook-transactions) or [`GET /v1/hooks/email/{id}/transactions`](https://developer.kustomer.com/api/apps-platform/get-email-hook-transactions) |

Use the `url` or `email` the API returns exactly as given; don't build it from the `hash`. Create bodies also accept optional `eventName`, `outputTemplate` and `outputSchema`, the same fields apps use to name a hook's event and shape what workflows receive. Give each system that posts to a web hook its own API key with only the `org.hooks` role, so a leaked key can't read your data. Hook lists return at most 99 hooks per page.

Related: [Inbound webhooks](https://developer.kustomer.com/docs/events/inbound-webhooks), [Outbound webhooks](https://developer.kustomer.com/api/apps-platform#outbound-web-hooks), [Workflows](https://developer.kustomer.com/docs/workflows), [Hooks in apps](https://developer.kustomer.com/docs/apps/workflows-actions-triggers#hooks-get-data-in)

### Recipes

#### 1. Create a web hook and the workflow that handles it

Give an external system, such as your store, an address to send events to, and a workflow that turns each one into Kustomer data.

**Step 1.** [`POST /v1/hooks/web`](https://developer.kustomer.com/api/apps-platform/create-web-hook.md). Create the hook. `title` is required. Keep `url`, where your system will post, and `eventName`, the trigger the workflow listens for.

```bash title="cURL"
curl --request POST "https://api.kustomerapp.com/v1/hooks/web" \
  --header "Authorization: Bearer $KUSTOMER_API_KEY" \
  --header "Content-Type: application/json" \
  --data '{"title": "Store orders", "description": "Order events from the storefront"}'
```

```bash title="kusty"
kusty api POST /v1/hooks/web --field title="Store orders" \
  --fields id,attributes.url,attributes.eventName
```

**Step 2.** [`POST /v1/workflows`](https://developer.kustomer.com/api/workflows/create-a-workflow.md). Create a workflow whose `trigger.eventName` is the hook's `eventName`. Step `1` is the transaction, and what you post is under `attributes.data`, so a field named `email` is `/#steps.1.attributes.data.email`. Leave it disabled until you've seen a real payload.

```json
{
  "name": "store-order-received",
  "trigger": {
    "id": "1",
    "eventName": "kustomer.hooks.web.<hookId>.receive",
    "transitions": [
      { "target": "find-customer", "condition": { "op": "exists", "values": ["/#steps.1.attributes.data.email"] } }
    ]
  },
  "steps": [
    {
      "id": "find-customer",
      "action": "kustomer.customer.find-by-email",
      "params": { "email": "/#steps.1.attributes.data.email" },
      "transitions": []
    }
  ]
}
```

```bash title="cURL"
curl --request POST "https://api.kustomerapp.com/v1/workflows" \
  --header "Authorization: Bearer $KUSTOMER_API_KEY" \
  --header "Content-Type: application/json" \
  --data @workflow.json
```

```bash title="kusty"
kusty api POST /v1/workflows --data @workflow.json --fields id
```

**Step 3.** [`POST /v1/hooks/web/{hash}`](https://developer.kustomer.com/api/apps-platform/create-web-hook-transaction.md). Post a test payload to the hook's `url` exactly as returned. Use a separate API key with only the `org.hooks` role, which can post to hooks and nothing else. The response is the new transaction's ID. Bodies over 400 KB fail with `413`.

```bash title="cURL"
curl --request POST "$HOOK_URL" \
  --header "Authorization: Bearer $KUSTOMER_HOOK_KEY" \
  --header "Content-Type: application/json" \
  --data '{"orderId": "4417", "email": "jane@example.com", "status": "shipped"}'
```

**Step 4.** [`PUT /v1/workflows/{id}`](https://developer.kustomer.com/api/workflows/update-a-workflow-based-on-the-id.md). Once the workflow does what you expect, enable it by sending only `enabled`.

```bash title="cURL"
curl --request PUT "https://api.kustomerapp.com/v1/workflows/$WORKFLOW_ID" \
  --header "Authorization: Bearer $KUSTOMER_API_KEY" \
  --header "Content-Type: application/json" \
  --data '{"enabled": true}'
```

```bash title="kusty"
kusty api PUT /v1/workflows/$WORKFLOW_ID --field enabled:=true
```

> The [inbound webhooks guide](https://developer.kustomer.com/docs/events/inbound-webhooks) covers form hooks for public web forms and email hooks for order emails.

#### 2. See exactly what a hook is receiving

Debug a sender whose data isn't showing up in the workflow, by looking at the raw transactions Kustomer stored.

**Step 1.** [`PATCH /v1/hooks/web/{id}`](https://developer.kustomer.com/api/apps-platform/update-web-hook.md). Turn on `debug`. Only transactions that arrive while it's on can be listed, so do this before the sender posts again.

```bash title="cURL"
curl --request PATCH "https://api.kustomerapp.com/v1/hooks/web/$HOOK_ID" \
  --header "Authorization: Bearer $KUSTOMER_API_KEY" \
  --header "Content-Type: application/json" \
  --data '{"debug": true}'
```

```bash title="kusty"
kusty api PATCH /v1/hooks/web/$HOOK_ID --field debug:=true
```

**Step 2.** [`GET /v1/hooks/web/{id}/transactions`](https://developer.kustomer.com/api/apps-platform/get-web-hook-transactions.md). List what arrived, with a key that has the `org.hooks` role. Each transaction's `attributes.data` is the payload as the workflow sees it, so compare its field names with the paths your workflow uses. Transactions stay visible for 24 hours.

```bash title="cURL"
curl "https://api.kustomerapp.com/v1/hooks/web/$HOOK_ID/transactions" \
  --header "Authorization: Bearer $KUSTOMER_API_KEY"
```

```bash title="kusty"
kusty api GET /v1/hooks/web/$HOOK_ID/transactions --all \
  | jq -c '[.data.attributes.createdAt, .data.attributes.data]'
```

**Step 3.** [`PATCH /v1/hooks/web/{id}`](https://developer.kustomer.com/api/apps-platform/update-web-hook.md). Turn `debug` off again when you're done.

```bash title="cURL"
curl --request PATCH "https://api.kustomerapp.com/v1/hooks/web/$HOOK_ID" \
  --header "Authorization: Bearer $KUSTOMER_API_KEY" \
  --header "Content-Type: application/json" \
  --data '{"debug": false}'
```

```bash title="kusty"
kusty api PATCH /v1/hooks/web/$HOOK_ID --field debug:=false
```

> Form and email hooks work the same way through `/v1/hooks/form/{id}` and `/v1/hooks/email/{id}`.

#### 3. Find the workflows a hook starts

Work out why data posted to a hook is accepted but nothing happens, or what would break if you retired the hook.

**Step 1.** [`GET /v1/hooks/web/{id}`](https://developer.kustomer.com/api/apps-platform/get-a-web-hook-by-id.md). Read the hook's `eventName`. It's usually `kustomer.hooks.web.<hookId>.receive`, but hooks that apps create can have their own, so read it rather than building it.

```bash title="cURL"
curl "https://api.kustomerapp.com/v1/hooks/web/$HOOK_ID" \
  --header "Authorization: Bearer $KUSTOMER_API_KEY"
```

```bash title="kusty"
HOOK_EVENT=$(kusty api GET /v1/hooks/web/$HOOK_ID | jq -r '.data.attributes.eventName')
```

**Step 2.** [`GET /v1/workflows`](https://developer.kustomer.com/api/workflows/get-workflows.md). List the workflows triggered by that event. If none come back, or none are `enabled`, the hook stores each request but nothing acts on it.

```bash title="cURL"
curl "https://api.kustomerapp.com/v1/workflows?eventName=$HOOK_EVENT" \
  --header "Authorization: Bearer $KUSTOMER_API_KEY"
```

```bash title="kusty"
kusty api GET /v1/workflows --query eventName=$HOOK_EVENT --all \
  | jq -r '[.data.id, .data.attributes.name, .data.attributes.enabled] | @tsv'
```

### Example object

A real record from [`POST /v1/hooks/web`](https://developer.kustomer.com/api/apps-platform/create-web-hook.md):

```json
{
  "id": "5825c452be3af71000ac6462",
  "type": "web_hook",
  "attributes": {
    "description": "My Amazing Webhook",
    "eventName": "kustomer.hooks.web.5825c452be3af71000ac6462.receive",
    "hash": "7733e0aab921040f15aa203d89ef8809a015c33d5303bd3642972d59602e43a6",
    "url": "https://api.kustomerapp.com/v1/hooks/web/7733e0aab921040f15aa203d89ef8809a015c33d5303bd3642972d59602e43a6",
    "debug": true,
    "createdAt": "2020-01-25T20:22:38.697Z",
    "modifiedAt": "2020-01-25T20:22:38.697Z",
    "updatedAt": "2020-01-25T20:22:38.697Z"
  },
  "relationships": {
    "org": {
      "data": {
        "type": "org",
        "id": "57f29863a1dbf61100e6aa92"
      },
      "links": {
        "self": "/v1/org/57f29863a1dbf61100e6aa92"
      },
      "createdBy": {
        "data": {
          "type": "user",
          "id": "5e18d1388552db001380cb26"
        },
        "links": {
          "self": "/v1/users/5e18d1388552db001380cb26"
        }
      },
      "modifiedBy": {
        "data": {
          "type": "user",
          "id": "5e18d1388552db001380cb26"
        },
        "links": {
          "self": "/v1/users/5e18d1388552db001380cb26"
        }
      }
    }
  },
  "links": {
    "self": "/v1/hooks/web/5825c452be3af71000ac6462"
  }
}
```

Fields:

- `attributes.description` (string): Free text for people, up to 4,096 characters. Change it, or `title`, with `PATCH`.
- `attributes.eventName` (string): The event each request fires, and the `trigger.eventName` a workflow uses to handle it. It's `kustomer.hooks.web.<hookId>.receive` unless an app gave the hook its own name.
- `attributes.hash` (string): The random, secret part of the hook's address. Don't build URLs from it; use `url` as returned.
- `attributes.url` (string): Where to `POST` data, with an API key that has the `org.hooks` role. Use it exactly as returned. Form hooks also have a `url`, which needs no key; email hooks have an `email` address instead.
- `attributes.debug` (boolean): When `true`, requests that arrive are kept for 24 hours and listed by the transactions endpoint. It's `false` unless you turn it on with `PATCH`; leave it off when you're not debugging.

### Permissions

Fewest roles for a read-only integration: `org.permission.hook.read`, `org.hooks`. For every endpoint: `org.permission.hook.read`, `org.permission.hook.create`, `org.permission.hook.update`, `org.hooks`.

## Query parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `page` | integer | no | Page number to return. |
| `pageSize` | integer | no | Number of records to return per page. |

## Responses

### 200

OK

- `meta` (object, required)
  - `pageSize` (integer, required)
  - `page` (integer, required)
- `data` (array<object>, required)
  - `id` (string, required)
  - `type` (string, required)
  - `attributes` (object, required)
    - `description` (string, required)
    - `eventName` (string, required)
    - `hash` (string, required)
    - `url` (string, required)
    - `createdAt` (string, required)
    - `modifiedAt` (string, required)
    - `updatedAt` (string, required)
  - `relationships` (object, required)
    - `org` (object, required)
  - `links` (object, required)
    - `self` (string, required)
- `links` (object, required)
  - `self` (string, required)
  - `first` (string, required)
  - `prev` (string, required, nullable)
  - `next` (string, required, nullable)

```json
{
  "meta": {
    "pageSize": 99,
    "page": 1
  },
  "data": [
    {
      "id": "5825c452be3af71000ac6462",
      "type": "web_hook",
      "attributes": {
        "description": "My Amazing Webhook",
        "eventName": "kustomer.hooks.web.5825c452be3af71000ac6462.receive",
        "hash": "7733e0aab921040f15aa203d89ef8809a015c33d5303bd3642972d59602e43a6",
        "url": "https://api.kustomerapp.com/v1/hooks/web/7733e0aab921040f15aa203d89ef8809a015c33d5303bd3642972d59602e43a6",
        "createdAt": "2020-01-25T20:22:38.697Z",
        "modifiedAt": "2020-01-25T20:22:38.697Z",
        "updatedAt": "2020-01-25T20:22:38.697Z"
      },
      "relationships": {
        "org": {
          "data": {
            "type": "org",
            "id": "57f29863a1dbf61100e6aa92"
          },
          "links": {
            "self": "/v1/org/57f29863a1dbf61100e6aa92"
          },
          "createdBy": {
            "data": {
              "type": "user",
              "id": "5e18d1388552db001380cb26"
            },
            "links": {
              "self": "/v1/users/5e18d1388552db001380cb26"
            }
          },
          "modifiedBy": {
            "data": {
              "type": "user",
              "id": "5e18d1388552db001380cb26"
            },
            "links": {
              "self": "/v1/users/5e18d1388552db001380cb26"
            }
          }
        }
      },
      "links": {
        "self": "/v1/hooks/web/5825c452be3af71000ac6462"
      }
    }
  ],
  "links": {
    "self": "/v1/hooks/web?page=1&pageSize=99",
    "first": "/v1/hooks/web?page=1&pageSize=99",
    "prev": null,
    "next": null
  }
}
```

### 400

Bad Request

### 401

Unauthorized

### 403

Forbidden

## Example request

```bash
curl --request GET \
  --url 'https://api.kustomerapp.com/v1/hooks/web' \
  --header "Authorization: Bearer $KUSTOMER_API_KEY"
```
