---
sidebar_label: Set up
toc_max_heading_level: 3
doc_id: b3f2a1d8-9c47-4e8b-a5f6-2d1e3b9c8f7a
description: >-
  Configure a notification channel, create a hook action, and implement the
  callback handler to start receiving entity hook events.
keywords:
  - entity hooks
  - notification channel
  - hook action
  - setup
  - callback
---

import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';

# Set up entity hooks

Setting up an entity hook involves three steps:

1. Configure a notification channel.
2. Create an entity hook action.
3. Implement the logic to process the hook and respond.

## 1. Configure a notification channel

Hooks send notifications through the specialized `entity` feed. To receive these notifications, configure a notification channel by sending a [POST request](/docs/api/notification-channel-create) with the endpoint details.

Here's an example for an HTTP notification channel:

<Tabs
    defaultValue="cli"
    values={[
        { label: 'CLI', value: 'cli' },
        { label: 'cURL', value: 'curl' },
    ]}>
<TabItem value="cli">

```bash
np notification channel create \
  --body '{
    "source": ["entity"],
    "nrn": "organization=1:account=2:namespace=3",
    "type": "http",
    "configuration": {
      "url": "https://yourdomain.com/url-you-configured"
    }
  }'
```

</TabItem>
<TabItem value="curl">

```bash
curl -L -X POST 'https://api.nullplatform.com/notification/channel' \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json' \
  -d '{
    "source": ["entity"],
    "nrn": "organization=1:account=2:namespace=3",
    "type": "http",
    "configuration": {
      "url": "https://yourdomain.com/url-you-configured"
    }
  }'
```

</TabItem>
</Tabs>

For more channel types and delivery options, see the [Notifications](/docs/notifications) documentation.

## 2. Create an entity hook action

An entity hook action declares that you want to receive notifications for a specific event on an entity type. [Create an entity hook action](/docs/api/entity-hook-action-create) with the event and scope you want to subscribe to:

<Tabs
    defaultValue="cli"
    values={[
        { label: 'CLI', value: 'cli' },
        { label: 'cURL', value: 'curl' },
    ]}>
<TabItem value="cli">

```bash
np entity-hook action create \
  --body '{
    "nrn": "organization=1:account=2:namespace=3:application=4",
    "entity": "scope",
    "action": "scope:create",
    "dimensions": {
      "environment": "staging",
      "country": "us"
    },
    "when": "before",
    "type": "hook",
    "on": "create"
  }'
```

</TabItem>
<TabItem value="curl">

```bash
curl -L -X POST 'https://api.nullplatform.com/entity_hook/action' \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json' \
  -d '{
    "nrn": "organization=1:account=2:namespace=3:application=4",
    "entity": "scope",
    "action": "scope:create",
    "dimensions": {
      "environment": "staging",
      "country": "us"
    },
    "when": "before",
    "type": "hook",
    "on": "create"
  }'
```

</TabItem>
</Tabs>

Required parameters:

- `entity`: The type of entity the hook applies to (`application`, `scope`, `deployment`).
- `action`: The event to subscribe to (for example, `application:create`, `scope:write`, `deployment:delete`).
- `when`: Whether the hook runs `before` or `after` nullplatform's internal processing.
- `type`: The nature of the hook. Currently only `hook` is supported.
- `on`: The lifecycle event that triggers the hook: `create`, `update`, or `delete`.

See the [Entity hook API](/docs/entity-hook-api-index) for the full parameter reference.

## 3. Implement hook processing

Once the hook action is configured, nullplatform starts sending notifications to your channel. Each notification carries a `callback_url` that points to the hook request: the record of that firing, which you can [read](/docs/api/entity-hook-request-read) at any time and [respond to](/docs/api/entity-hook-request-update) once your logic has run. Each notification has this format:

```json title="POST https://yourdomain.com/url-you-configured"
{
  "id": "1180cd02-1c36-4274-8e7a-4483b87e8f2e",
  "source": "entity",
  "event": "scope:create",
  "created_at": "2025-02-13T14:20:43.088Z",
  "notification": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "entity": "scope",
    "entity_id": "5",
    "nrn": "organization=1:account=2:namespace=3:application=4:scope=5",
    "callback_url": "https://api.nullplatform.com/entity_hook/550e8400-e29b-41d4-a716-446655440000",
    "type": "hook",
    "when": "before",
    "on": "create"
  }
}
```

### Responding to a hook

:::note
You need an [ops role](/docs/authorization/roles) to perform this action.
:::

After processing the hook, send a [`PATCH` request](/docs/api/entity-hook-request-update) to the `callback_url` from the notification:

```json title="PATCH callback-url"
{
  "status": "success",
  "messages": [
    {
      "level": "info",
      "message": "Information from the hook",
      "timestamp": 1740406453123
    },
    {
      "level": "warning",
      "message": "Warning report",
      "timestamp": 1740406527890
    },
    {
      "level": "error",
      "message": "The hook has failed",
      "timestamp": 1740406604567
    }
  ]
}
```

The `status` field controls how nullplatform handles the outcome:

| Status | Description |
|---|---|
| `success` | Hook logic executed successfully. The entity operation proceeds. |
| `failed` | Operation failed entirely. The entity enters an error state. |
| `recoverable_failure` | Partial failure. The entity remains valid but some changes may not have been applied. Relevant for update hooks. |
| `cancelled` | Entity operation halted because conditions were not met. |

The `messages` array is optional. Include it to give developers visibility into the hook's execution results.

### Updating the entity from the hook

A before-hook can also hand data back to the entity it's gating. Add a `callback_body` object to the same `PATCH` request. When nullplatform resumes the entity operation, it merges those fields into the update it sends to the entity.

```json title="PATCH callback-url"
{
  "status": "success",
  "callback_body": {
    "repository_url": "https://github.com/my-org/my-service",
    "tags": {
      "provisioned_by": "platform-hook"
    }
  }
}
```

For example, a before-hook on `application:create` can decide which repository the application lives in. The application is then created in the repository your hook chose, not in the one the developer typed.

Keep these rules in mind:

- `callback_body` accepts any field the entity's update endpoint accepts. There's no fixed list: whatever you'd send in a `PATCH` to that entity works here.
- Fields nullplatform sets itself win. The entity `status` that moves the operation forward can't be overridden from `callback_body`. You can add fields, but you can't change how nullplatform resumes the entity.
- `callback_body` must be a JSON object. Anything else is rejected with a `400`, and the hook stays pending so you can retry.
- It's stored with the hook request and returned when you [read the hook request](/docs/api/entity-hook-request-read), so you can always check what a hook handed back.
- It applies to whatever update runs for the `status` you send. Most hooks use it with `success`, but the merge also happens for `failed`, `recoverable_failure`, and `cancelled`.

## Optional: filter which events reach your endpoint

By default, the hook fires on every matching event for the `entity`, `action`, and `dimensions` you set. If you want to react only to some of those events (for example, only when a deployment fully switches traffic, or only when a scope is being stopped), you don't filter the hook itself. You filter the **notification channel** that the hook delivers through.

The hook stays generic; the channel decides which events get through. See [Conditional firing](./filters) for the filter syntax, supported operators, and worked examples.
