# Get Email Hooks

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

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

```http
GET https://{orgname}.api.kustomerapp.com/v1/hooks/email
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 existing email hooks.

An email hook allows you to pass data into Kustomer by sending HTML email. Creating an email hook will return an email address.

Any email received at that address will be parsed to extract json data. For added security, the email data requires inclusion of
a key.

The key should be included in the script tag within the html body of the email message along with the script type, i.e. ```<script type="application/ld+json" data-kustomer-key="KEY_HERE">```

 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)
    - `title` (string, required)
    - `description` (string, required)
    - `email` (string, required)
    - `eventName` (string, required)
    - `hash` (string, required)
    - `key` (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": "5823f36ef0e79711005cd5c6",
      "type": "email_hook",
      "attributes": {
        "title": "Foobar.IO",
        "description": "foobar.io integration email hook",
        "email": "e2eaa64bb7ce43801e6af1d7e3b4655abbaa501ee238865070de5a1896987c9a@hooks.kustomerapp.com",
        "eventName": "kustomer.hooks.email.5823f36ef0e79711005cd5c6.receive",
        "hash": "e2eaa64bb7ce43801e6af1d7e3b4655abbaa501ee238865070de5a1896987c9a",
        "key": "d28283b7b1cbcf8ab0c0",
        "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/email/5823f36ef0e79711005cd5c6"
      }
    }
  ],
  "links": {
    "self": "/v1/hooks/email?page=1&pageSize=99",
    "first": "/v1/hooks/email?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/email' \
  --header "Authorization: Bearer $KUSTOMER_API_KEY"
```
