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

# Interactive Notifications

> Send notifications with response buttons and collect user responses.

## Overview

Interactive notifications display a message with clickable buttons. Users respond directly in Slack or Teams, and you can track responses via polling or callbacks.

Common use cases:

* Deployment approvals
* Incident acknowledgement
* Shift confirmations
* Survey questions

## Endpoint

```
POST /api/notify
```

## Request body

Include the `response_request` field alongside the standard notification fields.

<ParamField body="user_emails" type="string[]" required>
  Recipients. Maximum 50.
</ParamField>

<ParamField body="message" type="string" required>
  The prompt shown above the buttons. Maximum 4000 characters.
</ParamField>

<ParamField body="external_reference_id" type="string">
  Your correlation ID. Maximum 200 characters.
</ParamField>

<ParamField body="response_request" type="object" required>
  Configures the interactive behavior.

  <Expandable title="response_request fields">
    <ParamField body="response_request.options" type="array" required>
      2 to 5 response buttons.

      Each option has:

      * `text` (string, 1-75 chars) — Button label shown to the user
      * `value` (string, 1-100 chars) — Machine-readable value returned in responses
    </ParamField>

    <ParamField body="response_request.timeout_minutes" type="number" required>
      Minutes before the request expires. Range: 1-1440 (24 hours).

      After expiry, unresponded users are marked as expired and buttons are disabled.
    </ParamField>

    <ParamField body="response_request.disable" type="string" default="onUserAction">
      When to disable buttons for a given user:

      * `onUserAction` — Buttons are disabled immediately after the user clicks. The request completes when all users have responded or the timeout is reached.
      * `onTimeout` — Buttons remain active until timeout. Users can change their response. Final values are captured at expiry.
    </ParamField>

    <ParamField body="response_request.callback_url" type="string">
      HTTPS URL to receive a POST with the final results when the request completes.

      Must be publicly reachable. Validated on submission (DNS resolution, HTTPS required, no private IPs). See [Callbacks](/api-reference/callbacks).
    </ParamField>
  </Expandable>
</ParamField>

## Response

<ResponseField name="status" type="string">
  `"ok"` on success.
</ResponseField>

<ResponseField name="request_id" type="string">
  Unique identifier for this notification request. Use it to [check status](/api-reference/check-status).
</ResponseField>

<ResponseField name="callback_secret" type="string">
  Only present when `callback_url` is provided. Use this to [verify callback signatures](/api-reference/callbacks#signature-verification). Store it securely — it is only returned once.
</ResponseField>

```json theme={null}
{
  "status": "ok",
  "request_id": "req_a1b2c3d4e5f6...",
  "callback_secret": "dGhpc19pc19hX3NlY3JldA..."
}
```

## Example: deployment approval

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://app.nextks.com/api/notify \
    -H "Content-Type: application/json" \
    -H "X-API-Key: YOUR_API_KEY" \
    -d '{
      "user_emails": ["lead@company.com"],
      "message": "Release v2.4.1 is ready.\nChanges: 3 bug fixes, 1 new feature.\n\nDeploy to production?",
      "external_reference_id": "deploy-v2.4.1",
      "response_request": {
        "options": [
          { "text": "Approve", "value": "approve" },
          { "text": "Reject", "value": "reject" },
          { "text": "Delay 1h", "value": "delay" }
        ],
        "timeout_minutes": 60,
        "callback_url": "https://ci.company.com/hooks/nextks"
      }
    }'
  ```

  ```typescript Node.js theme={null}
  const response = await fetch('https://app.nextks.com/api/notify', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'X-API-Key': 'YOUR_API_KEY',
    },
    body: JSON.stringify({
      user_emails: ['lead@company.com'],
      message: 'Release v2.4.1 is ready.\nChanges: 3 bug fixes, 1 new feature.\n\nDeploy to production?',
      external_reference_id: 'deploy-v2.4.1',
      response_request: {
        options: [
          { text: 'Approve', value: 'approve' },
          { text: 'Reject', value: 'reject' },
          { text: 'Delay 1h', value: 'delay' },
        ],
        timeout_minutes: 60,
        callback_url: 'https://ci.company.com/hooks/nextks',
      },
    }),
  })

  const data = await response.json()
  // Store the callback_secret to verify incoming callbacks
  console.log(data.request_id)       // "req_a1b2c3d4e5f6..."
  console.log(data.callback_secret)  // "dGhpc19pc19hX3NlY3JldA..."
  ```

  ```python Python theme={null}
  import requests

  response = requests.post(
      "https://app.nextks.com/api/notify",
      headers={
          "Content-Type": "application/json",
          "X-API-Key": "YOUR_API_KEY",
      },
      json={
          "user_emails": ["lead@company.com"],
          "message": "Release v2.4.1 is ready.\nChanges: 3 bug fixes, 1 new feature.\n\nDeploy to production?",
          "external_reference_id": "deploy-v2.4.1",
          "response_request": {
              "options": [
                  {"text": "Approve", "value": "approve"},
                  {"text": "Reject", "value": "reject"},
                  {"text": "Delay 1h", "value": "delay"},
              ],
              "timeout_minutes": 60,
              "callback_url": "https://ci.company.com/hooks/nextks",
          },
      },
  )

  data = response.json()
  # Store the callback_secret to verify incoming callbacks
  print(data["request_id"])       # "req_a1b2c3d4e5f6..."
  print(data["callback_secret"])  # "dGhpc19pc19hX3NlY3JldA..."
  ```
</CodeGroup>

## Message appearance

The notification renders as:

1. **Message text** — Your `message` content with markdown formatting
2. **Response buttons** — One button per option
3. **Footer** — Shows validity period and sender name (e.g., *"Valid for 1h. Sent by Alice via Notifications API."*)

After a user responds (in `onUserAction` mode), the message is updated to show their choice and the buttons are removed.

## Disable modes

<Tabs>
  <Tab title="onUserAction (default)">
    * Buttons disable immediately when the user clicks
    * The user's choice is final
    * Request completes when all users respond or timeout is reached
    * Best for: approvals, acknowledgements — one-shot decisions
  </Tab>

  <Tab title="onTimeout">
    * Buttons stay active until timeout
    * Users can click multiple times; only the last response counts
    * All responses are captured at timeout
    * Best for: polls, surveys — allow users to change their mind
  </Tab>
</Tabs>
