# Knock

> Send the notifications you designed in Knock from a workflow: trigger a workflow by key for up to a thousand recipients at once, with the data its templates fill themselves in from. Keep the people and records Knock notifies up to date, read and write their channel and category preferences, group them by tenant, and look back over everything that was sent and what the recipients did with it.

App · Notifications · 20 actions. Web page: https://docs.lodol.com/docs/integrations/knock

## Actions

### Cancel a workflow run

Stop a workflow run that is still waiting, whether in a delay step or gathering a batch. Anything already sent stays sent. Give it the same cancellation key the trigger step used.

**Inputs**

- Workflow key (`workflow_key`, text, required): The workflow whose run you want to stop.
- Cancellation key (`cancellation_key`, text, required): The key the 'Trigger a workflow' step was given.
- Recipients (`recipients`, list): Cancel for these recipients only. Leave blank to cancel for everyone the key covers.

**Outputs**

- `cancellation_key` (text)
- `cancelled` (true/false)
- `recipient_count` (number)
- `workflow_key` (text)

**Example output**

```json
{
  "cancellation_key": "comment-91",
  "cancelled": true,
  "recipient_count": 0,
  "workflow_key": "new-comment"
}
```

### Delete a tenant

Delete a tenant and everything associated with it. This cannot be undone.

**Inputs**

- Tenant ID (`tenant_id`, text, required): Which tenant to delete.

**Outputs**

- `deleted` (true/false)
- `tenant_id` (text)

**Example output**

```json
{
  "deleted": true,
  "tenant_id": "acme"
}
```

### Delete a user

Permanently delete a user and all of their data, including their messages and preferences. This cannot be undone.

**Inputs**

- User ID (`user_id`, text, required): Your own id for the person, exactly as you send it to Knock. Knock stores whatever you use, so this is usually your database id.

**Outputs**

- `deleted` (true/false)
- `user_id` (text)

**Example output**

```json
{
  "deleted": true,
  "user_id": "user_1"
}
```

### Delete an object

Delete one record and everything associated with it, including the subscriptions to it. This cannot be undone.

**Inputs**

- Collection (`collection`, text, required): The kind of record this is: Knock creates the collection the first time you write to it, so any name you use consistently works.
- Object ID (`object_id`, text, required): Your own id for this record, unique within the collection.

**Outputs**

- `collection` (text)
- `deleted` (true/false)
- `object_id` (text)

**Example output**

```json
{
  "collection": "projects",
  "deleted": true,
  "object_id": "project-42"
}
```

### Get a message

Read one notification: which channel carried it, whether it was delivered, and whether the recipient saw, read or clicked it.

**Inputs**

- Message ID (`message_id`, text, required): The id of one notification. A 'List messages' step returns these, and the Messages page in Knock shows them.

**Outputs**

- `archived_at` (text)
- `channel_id` (text)
- `channel_name` (text)
- `channel_type` (text)
- `data` (object)
- `engagement_statuses` (list)
- `id` (text)
- `inserted_at` (text)
- `link_clicked_at` (text)
- `read_at` (text)
- `recipient`
- `scheduled_at` (text)
- `seen_at` (text)
- `status` (text)
- `tenant` (text)
- `updated_at` (text)
- `workflow` (text)
- `workflow_run_id` (text)

**Example output**

```json
{
  "archived_at": null,
  "channel_id": "8f2a1c3e-0b4d-4f7a-9c1e-2d3b4a5c6d7e",
  "channel_name": "Transactional email",
  "channel_type": "email",
  "data": {
    "plan": "pro"
  },
  "engagement_statuses": [
    "seen"
  ],
  "id": "2p3q4r5s6t7u8v9w0x",
  "inserted_at": "2026-09-18T09:00:00.000Z",
  "link_clicked_at": null,
  "read_at": null,
  "recipient": "user_1",
  "scheduled_at": null,
  "seen_at": "2026-09-18T09:04:00.000Z",
  "status": "delivered",
  "tenant": "acme",
  "updated_at": "2026-09-18T09:00:05.000Z",
  "workflow": "welcome-email",
  "workflow_run_id": "1a2b3c4d5e6f7g8h9i"
}
```

### Get a tenant

Read one tenant and its settings.

**Inputs**

- Tenant ID (`tenant_id`, text, required): Which tenant to read.
- Resolve full preferences (`resolve_full_preferences`, true/false): Also merge the environment's default preferences into the tenant's preference set, so you see what would actually apply. Default: `false`.

**Outputs**

- `id` (text)
- `name` (text)
- `properties` (object)
- `settings` (object)

**Example output**

```json
{
  "id": "acme",
  "name": "Acme Inc",
  "properties": {
    "industry": "manufacturing"
  },
  "settings": {}
}
```

### Get a user

Read one Knock user and everything stored on them.

**Inputs**

- User ID (`user_id`, text, required): Your own id for the person, exactly as you send it to Knock. Knock stores whatever you use, so this is usually your database id.

**Outputs**

- `avatar` (text)
- `created_at` (text)
- `email` (text)
- `id` (text)
- `locale` (text)
- `name` (text)
- `phone_number` (text)
- `properties` (object)
- `timezone` (text)
- `updated_at` (text)

**Example output**

```json
{
  "avatar": "https://example.com/ada.png",
  "created_at": "2026-09-18T09:00:00.000Z",
  "email": "ada@example.com",
  "id": "user_1",
  "locale": "en-GB",
  "name": "Ada Lovelace",
  "phone_number": "+15550001111",
  "properties": {
    "plan": "pro"
  },
  "timezone": "Europe/London",
  "updated_at": "2026-09-18T09:00:00.000Z"
}
```

### Get an object

Read one record out of a collection.

**Inputs**

- Collection (`collection`, text, required): The kind of record this is: Knock creates the collection the first time you write to it, so any name you use consistently works.
- Object ID (`object_id`, text, required): Your own id for this record, unique within the collection.

**Outputs**

- `collection` (text)
- `created_at` (text)
- `id` (text)
- `properties` (object)
- `updated_at` (text)

**Example output**

```json
{
  "collection": "projects",
  "created_at": "2026-09-18T09:00:00.000Z",
  "id": "project-42",
  "properties": {
    "name": "Apollo",
    "owner": "user_1"
  },
  "updated_at": "2026-09-18T09:00:00.000Z"
}
```

### Identify a user

Create a Knock user, or update one that already exists. Only the fields you fill in are changed, so this is safe to run on every sign-in. There is no separate create step.

**Inputs**

- User ID (`user_id`, text, required): Your own id for the person, exactly as you send it to Knock. Knock stores whatever you use, so this is usually your database id.
- Name (`name`, text): What templates should call them.
- Email (`email`, text): Where email notifications go.
- Phone number (`phone_number`, text): In E.164 form, for SMS: '+15550001111'.
- Avatar URL (`avatar`, text): A picture of them, for in-app feeds.
- Time zone (`timezone`, text): A tz database name such as 'America/New_York'. Knock uses it for send windows and for local times in templates.
- Locale (`locale`, text): A language tag such as 'en-GB' or 'fr'.
- Custom properties (`properties`, object): Anything else you want stored on the user and usable in templates and conditions.

**Outputs**

- `avatar` (text)
- `created_at` (text)
- `email` (text)
- `id` (text)
- `locale` (text)
- `name` (text)
- `phone_number` (text)
- `properties` (object)
- `timezone` (text)
- `updated_at` (text)

**Example output**

```json
{
  "avatar": "https://example.com/ada.png",
  "created_at": "2026-09-18T09:00:00.000Z",
  "email": "ada@example.com",
  "id": "user_1",
  "locale": "en-GB",
  "name": "Ada Lovelace",
  "phone_number": "+15550001111",
  "properties": {
    "plan": "pro"
  },
  "timezone": "Europe/London",
  "updated_at": "2026-09-18T09:00:00.000Z"
}
```

### List a user's messages

Every notification sent to one person, newest first, with filters for how it was delivered and what they did with it.

**Inputs**

- User ID (`user_id`, text, required): Your own id for the person, exactly as you send it to Knock. Knock stores whatever you use, so this is usually your database id.
- Tenant (`tenant`, text): Only messages sent for this tenant.
- Delivery status (`status`, text): Only messages in these delivery states. One or more of: `queued` (Queued — accepted, not yet sent), `sent` (Sent to the channel provider), `delivered` (Delivered to the recipient), `delivery_attempted` (Delivery attempted), `undelivered`, `not_sent` (Not sent), `bounced`.
- Engagement (`engagement_status`, text): Only messages the recipient engaged with in these ways. One or more of: `seen`, `unseen` (Not seen), `read`, `unread` (Not read), `archived`, `unarchived` (Not archived), `link_clicked` (A link was clicked), `interacted` (Interacted with).
- Workflow key (`source`, text): Only messages sent by this workflow.
- Workflow run ID (`workflow_run_id`, text): Only the messages from one run: the value a 'Trigger a workflow' step hands back.
- Channel ID (`channel_id`, text): Only messages sent through this Knock channel.
- Created after (`inserted_after`, date and time): Only messages created at or after this moment.
- Created before (`inserted_before`, date and time): Only messages created at or before this moment.
- How many per page (`page_size`, number): Defaults to 50 if left blank. From 1 to 100.
- Next page cursor (`after`, text): Pass the 'after' value from a previous run of this step to get the next page. Knock pages by cursor, not by page number.
- Previous page cursor (`before`, text): The 'before' value from a previous run of this step.

**Outputs**

- `after` (text)
- `before` (text)
- `count` (number)
- `has_more` (true/false)
- `items` (list)

**Example output**

```json
{
  "after": null,
  "before": null,
  "count": 1,
  "has_more": false,
  "items": [
    {
      "archived_at": null,
      "channel_id": "8f2a1c3e-0b4d-4f7a-9c1e-2d3b4a5c6d7e",
      "channel_name": "Transactional email",
      "channel_type": "email",
      "data": {
        "plan": "pro"
      },
      "engagement_statuses": [
        "seen"
      ],
      "id": "2p3q4r5s6t7u8v9w0x",
      "inserted_at": "2026-09-18T09:00:00.000Z",
      "link_clicked_at": null,
      "read_at": null,
      "recipient": "user_1",
      "scheduled_at": null,
      "seen_at": "2026-09-18T09:04:00.000Z",
      "status": "delivered",
      "tenant": "acme",
      "updated_at": "2026-09-18T09:00:05.000Z",
      "workflow": "welcome-email",
      "workflow_run_id": "1a2b3c4d5e6f7g8h9i"
    }
  ]
}
```

### List a user's preferences

Every preference set stored for one user. Most accounts have one, called 'default'; a user who has never set anything has none, which is not the same as having opted out.

**Inputs**

- User ID (`user_id`, text, required): Your own id for the person, exactly as you send it to Knock. Knock stores whatever you use, so this is usually your database id.

**Outputs**

- `count` (number)
- `preference_sets` (list)
- `user_id` (text)

**Example output**

```json
{
  "count": 1,
  "preference_sets": [
    {
      "categories": {
        "marketing": false
      },
      "channel_types": {
        "email": true,
        "sms": false
      },
      "channels": null,
      "commercial_subscribed": true,
      "id": "default",
      "workflows": null
    }
  ],
  "user_id": "user_1"
}
```

### List messages

Every notification this Knock environment has sent, newest first, with filters for the workflow, the channel, how it was delivered and what the recipient did with it.

**Inputs**

- Tenant (`tenant`, text): Only messages sent for this tenant.
- Delivery status (`status`, text): Only messages in these delivery states. One or more of: `queued` (Queued — accepted, not yet sent), `sent` (Sent to the channel provider), `delivered` (Delivered to the recipient), `delivery_attempted` (Delivery attempted), `undelivered`, `not_sent` (Not sent), `bounced`.
- Engagement (`engagement_status`, text): Only messages the recipient engaged with in these ways. One or more of: `seen`, `unseen` (Not seen), `read`, `unread` (Not read), `archived`, `unarchived` (Not archived), `link_clicked` (A link was clicked), `interacted` (Interacted with).
- Workflow key (`source`, text): Only messages sent by this workflow.
- Workflow run ID (`workflow_run_id`, text): Only the messages from one run: the value a 'Trigger a workflow' step hands back.
- Channel ID (`channel_id`, text): Only messages sent through this Knock channel.
- Created after (`inserted_after`, date and time): Only messages created at or after this moment.
- Created before (`inserted_before`, date and time): Only messages created at or before this moment.
- How many per page (`page_size`, number): Defaults to 50 if left blank. From 1 to 100.
- Next page cursor (`after`, text): Pass the 'after' value from a previous run of this step to get the next page. Knock pages by cursor, not by page number.
- Previous page cursor (`before`, text): The 'before' value from a previous run of this step.

**Outputs**

- `after` (text)
- `before` (text)
- `count` (number)
- `has_more` (true/false)
- `items` (list)

**Example output**

```json
{
  "after": null,
  "before": null,
  "count": 1,
  "has_more": false,
  "items": [
    {
      "archived_at": null,
      "channel_id": "8f2a1c3e-0b4d-4f7a-9c1e-2d3b4a5c6d7e",
      "channel_name": "Transactional email",
      "channel_type": "email",
      "data": {
        "plan": "pro"
      },
      "engagement_statuses": [
        "seen"
      ],
      "id": "2p3q4r5s6t7u8v9w0x",
      "inserted_at": "2026-09-18T09:00:00.000Z",
      "link_clicked_at": null,
      "read_at": null,
      "recipient": "user_1",
      "scheduled_at": null,
      "seen_at": "2026-09-18T09:04:00.000Z",
      "status": "delivered",
      "tenant": "acme",
      "updated_at": "2026-09-18T09:00:05.000Z",
      "workflow": "welcome-email",
      "workflow_run_id": "1a2b3c4d5e6f7g8h9i"
    }
  ]
}
```

### List objects

List the records in one collection, a page at a time. Pass the 'after' cursor from one run into the next.

**Inputs**

- Collection (`collection`, text, required): The kind of record this is: Knock creates the collection the first time you write to it, so any name you use consistently works.
- Include preferences (`include_preferences`, true/false): Also return each record's notification preferences. Default: `false`.
- How many per page (`page_size`, number): Defaults to 50 if left blank. From 1 to 100.
- Next page cursor (`after`, text): Pass the 'after' value from a previous run of this step to get the next page. Knock pages by cursor, not by page number.
- Previous page cursor (`before`, text): The 'before' value from a previous run of this step.

**Outputs**

- `after` (text)
- `before` (text)
- `count` (number)
- `has_more` (true/false)
- `items` (list)

**Example output**

```json
{
  "after": null,
  "before": null,
  "count": 1,
  "has_more": false,
  "items": [
    {
      "collection": "projects",
      "created_at": "2026-09-18T09:00:00.000Z",
      "id": "project-42",
      "properties": {
        "name": "Apollo",
        "owner": "user_1"
      },
      "updated_at": "2026-09-18T09:00:00.000Z"
    }
  ]
}
```

### List tenants

List the tenants in this Knock environment, optionally filtered by name or id.

**Inputs**

- Name contains (`name`, text): Only tenants whose name matches.
- Tenant ID (`tenant_id`, text): Only the tenant with this id.
- How many per page (`page_size`, number): Defaults to 50 if left blank. From 1 to 100.
- Next page cursor (`after`, text): Pass the 'after' value from a previous run of this step to get the next page. Knock pages by cursor, not by page number.
- Previous page cursor (`before`, text): The 'before' value from a previous run of this step.

**Outputs**

- `after` (text)
- `before` (text)
- `count` (number)
- `has_more` (true/false)
- `items` (list)

**Example output**

```json
{
  "after": null,
  "before": null,
  "count": 1,
  "has_more": false,
  "items": [
    {
      "id": "acme",
      "name": "Acme Inc",
      "properties": {
        "industry": "manufacturing"
      },
      "settings": {}
    }
  ]
}
```

### List users

List the users in this Knock environment, a page at a time. Pass the 'after' cursor from one run into the next to keep going.

**Inputs**

- Include preferences (`include_preferences`, true/false): Also return each record's notification preferences. Default: `false`.
- How many per page (`page_size`, number): Defaults to 50 if left blank. From 1 to 100.
- Next page cursor (`after`, text): Pass the 'after' value from a previous run of this step to get the next page. Knock pages by cursor, not by page number.
- Previous page cursor (`before`, text): The 'before' value from a previous run of this step.

**Outputs**

- `after` (text)
- `before` (text)
- `count` (number)
- `has_more` (true/false)
- `items` (list)

**Example output**

```json
{
  "after": null,
  "before": null,
  "count": 1,
  "has_more": false,
  "items": [
    {
      "avatar": "https://example.com/ada.png",
      "created_at": "2026-09-18T09:00:00.000Z",
      "email": "ada@example.com",
      "id": "user_1",
      "locale": "en-GB",
      "name": "Ada Lovelace",
      "phone_number": "+15550001111",
      "properties": {
        "plan": "pro"
      },
      "timezone": "Europe/London",
      "updated_at": "2026-09-18T09:00:00.000Z"
    }
  ]
}
```

### Mark a message as read

Mark an in-app message read on the recipient's behalf, for when they acted on it somewhere other than the feed.

**Inputs**

- Message ID (`message_id`, text, required): The id of one notification. A 'List messages' step returns these, and the Messages page in Knock shows them.

**Outputs**

- `archived_at` (text)
- `channel_id` (text)
- `channel_name` (text)
- `channel_type` (text)
- `data` (object)
- `engagement_statuses` (list)
- `id` (text)
- `inserted_at` (text)
- `link_clicked_at` (text)
- `read_at` (text)
- `recipient`
- `scheduled_at` (text)
- `seen_at` (text)
- `status` (text)
- `tenant` (text)
- `updated_at` (text)
- `workflow` (text)
- `workflow_run_id` (text)

**Example output**

```json
{
  "archived_at": null,
  "channel_id": "8f2a1c3e-0b4d-4f7a-9c1e-2d3b4a5c6d7e",
  "channel_name": "Transactional email",
  "channel_type": "email",
  "data": {
    "plan": "pro"
  },
  "engagement_statuses": [
    "seen"
  ],
  "id": "2p3q4r5s6t7u8v9w0x",
  "inserted_at": "2026-09-18T09:00:00.000Z",
  "link_clicked_at": null,
  "read_at": "2026-09-18T09:05:00.000Z",
  "recipient": "user_1",
  "scheduled_at": null,
  "seen_at": "2026-09-18T09:04:00.000Z",
  "status": "delivered",
  "tenant": "acme",
  "updated_at": "2026-09-18T09:00:05.000Z",
  "workflow": "welcome-email",
  "workflow_run_id": "1a2b3c4d5e6f7g8h9i"
}
```

### Set a tenant

Create or update a tenant: the customer or workspace a notification belongs to. The record, its name and its custom properties work on every Knock plan; the branding fields and tenant-level preference defaults are an Enterprise feature and Knock refuses them otherwise.

**Inputs**

- Tenant ID (`tenant_id`, text, required): Your own id for the customer or workspace. Writing to an id that doesn't exist yet creates it.
- Name (`name`, text): What to call the tenant in templates.
- Branding (Enterprise) (`branding`, object): icon_url, logo_url, primary_color and primary_color_contrast. Only applied on Knock's Enterprise plan: leave it blank otherwise.
- Custom properties (`properties`, object): Anything else to store on the tenant.

**Outputs**

- `id` (text)
- `name` (text)
- `properties` (object)
- `settings` (object)

**Example output**

```json
{
  "id": "acme",
  "name": "Acme Inc",
  "properties": {
    "industry": "manufacturing"
  },
  "settings": {}
}
```

### Set a user's preferences

Write what a user does and doesn't want to be notified about, by channel type, by category or by workflow. Knock's own default replaces everything not sent, so this step defaults to merging instead: switch it to Replace only when you mean to discard the rest.

**Inputs**

- User ID (`user_id`, text, required): Your own id for the person, exactly as you send it to Knock. Knock stores whatever you use, so this is usually your database id.
- How to apply this (`persistence_strategy`, text): Merge keeps preferences this step doesn't mention. Replace discards them. One of: `merge` (Merge — keep preferences this step doesn't mention), `replace` (Replace — discard every preference this step doesn't send). Default: `merge`.
- Channel types (`channel_types`, object): True or false per channel type: email, in_app_feed, sms, push, chat, http.
- Categories (`categories`, object): True or false per workflow category, keyed by the category key from Knock.
- Workflows (`workflows`, object): True or false per workflow key.
- Channels (`channels`, object): True or false per Knock channel id.
- Subscribed to commercial messages (`commercial_subscribed`, true/false): Whether they accept marketing notifications.
- Preference set (`preference_set_id`, text): Leave blank for 'default', which is the set Knock consults unless a workflow names another. Default: `default`.

**Outputs**

- `categories` (object)
- `channel_types` (object)
- `channels` (object)
- `commercial_subscribed` (true/false)
- `id` (text)
- `workflows` (object)

**Example output**

```json
{
  "categories": {
    "marketing": false
  },
  "channel_types": {
    "email": true,
    "sms": false
  },
  "channels": null,
  "commercial_subscribed": true,
  "id": "default",
  "workflows": null
}
```

### Set an object

Create or update a record that isn't a person: a project, a document, a repository. Objects can be subscribed to and notified just like users. Only the fields you fill in change.

**Inputs**

- Collection (`collection`, text, required): The kind of record this is: Knock creates the collection the first time you write to it, so any name you use consistently works.
- Object ID (`object_id`, text, required): Your own id for this record, unique within the collection.
- Name (`name`, text): What templates should call this record.
- Time zone (`timezone`, text): A tz database name, for send windows.
- Locale (`locale`, text): A language tag such as 'en-GB'.
- Custom properties (`properties`, object): Anything else to store on the record. These come back nested under 'properties' when you read it.

**Outputs**

- `collection` (text)
- `created_at` (text)
- `id` (text)
- `properties` (object)
- `updated_at` (text)

**Example output**

```json
{
  "collection": "projects",
  "created_at": "2026-09-18T09:00:00.000Z",
  "id": "project-42",
  "properties": {
    "name": "Apollo",
    "owner": "user_1"
  },
  "updated_at": "2026-09-18T09:00:00.000Z"
}
```

### Trigger a workflow

Run one of your Knock workflows: Knock decides the channels, the template and the batching from what you built in the dashboard. The workflow must be committed to the environment this connection's key belongs to before the API can trigger it, in development as well as production.

**Inputs**

- Workflow key (`workflow_key`, text, required): The key shown under the workflow's name in Knock. Not the display name: 'new-comment', not 'New comment'.
- Recipients (`recipients`, list, required): Who to notify: user ids, or whole user objects to identify and notify in one step. Up to 1,000.
- Data (`data`, object): What the template fills itself in with: every variable your workflow references.
- Actor (`actor`, text): Who caused this, if anybody. A user id, or a whole user object to identify at the same time.
- Tenant (`tenant`, text): The customer or workspace this notification belongs to. Used for per-tenant branding and preferences.
- Cancellation key (`cancellation_key`, text): Your own id for this run, so a later 'Cancel a workflow run' step can stop it while it is still waiting in a delay or batch.
- Idempotency key (`idempotency_key`, text): Send the same key twice and Knock only runs the workflow once. Use it when a retry must not notify anybody twice.
- Sandbox mode (`sandbox_mode`, true/false): Run the workflow without actually delivering anything. Messages are still recorded. Default: `false`.
- Skip delays (`skip_delay`, true/false): Run the workflow's delay steps through immediately. Default: `false`.

**Outputs**

- `cancellation_key` (text)
- `recipient_count` (number)
- `workflow_key` (text)
- `workflow_run_id` (text)

**Example output**

```json
{
  "cancellation_key": null,
  "recipient_count": 1,
  "workflow_key": "new-comment",
  "workflow_run_id": "1a2b3c4d5e6f7g8h9i"
}
```
