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

# Create a trigger inbox for an app

> Connect to Slack, Gmail, and 9,000+ other apps using the user's app connection and the Trigger Inbox API.

# Create a trigger inbox for an app

This tutorial covers the [Trigger Inbox API](/white-label/trigger-inbox/what-is-trigger-inbox-api) connected to an app using the user's app connection. It follows the same queue model as [Trigger Inbox quickstart](/white-label/trigger-inbox/trigger-inbox-quickstart), adding connection setup before inbox creation.

## Before you begin

* An access token set as `$TOKEN`. [Token exchange](/white-label/implementation/token-exchange) covers how to get one.
* A connected app account for the user. [Connection flow](/white-label/implementation/connection-flow) covers setup. You will gather everything else in [Step 1](#step-1-gather-your-subscription-values).

***

## Step 1: Gather your subscription values

Creating an inbox for an app needs three values from the [Workflow API](/powered-by-zapier/api-reference/apps/get-apps-\[v2]), all authenticated with the same `$TOKEN` as the Trigger Inbox API, plus your trigger's input fields:

* **`app_key`**: the app's key, from `GET /v2/apps`. Accepts the versionless key (e.g. `SlackCLIAPI`), a slug (e.g. `slack`), or `@latest` suffix (e.g. `SlackCLIAPI@latest`). All resolve to the latest version automatically.
* **`action_key`**: the trigger's key, from `GET /v2/actions`. Identifies which trigger the inbox subscribes to.
* **`connection_id`**: the ID of the user's connected account, from `GET /v2/authentications`.

### 1. Find the app key

List apps and read the `key` field. The `query` parameter narrows the list by app title, but returns partial matches, you may get more than one result. Match the `title` field to identify the correct app.

<CodeGroup>
  ```bash cURL theme={null}
  curl -s "https://api.zapier.com/v2/apps?query=Slack" \
    -H "Authorization: Bearer $TOKEN" | python3 -m json.tool
  ```

  ```typescript TypeScript theme={null}
  const res = await fetch("https://api.zapier.com/v2/apps?query=Slack", {
    headers: { Authorization: `Bearer ${TOKEN}` },
  });
  const { data } = await res.json();
  console.log(data.map((app) => ({ key: app.key, id: app.id, title: app.title })));
  ```

  ```python Python theme={null}
  resp = requests.get(
      "https://api.zapier.com/v2/apps",
      params={"query": "Slack"},
      headers={"Authorization": f"Bearer {TOKEN}"},
  )
  resp.raise_for_status()
  for app in resp.json()["data"]:
      print(app["key"], app["id"], app["title"])
  ```
</CodeGroup>

Each app has a versionless `key` and a UUID `id`:

```json theme={null}
{
  "data": [
    {
      "id": "81f613aa-c98a-4383-a5fc-195e68647217",
      "key": "SlackCLIAPI",
      "type": "app",
      "title": "Slack"
    }
  ]
}
```

Use the `key` (for example, `SlackCLIAPI`) as your `app_key`. Replace `APP_ID` in the next steps with the `id` value from the response.

### 2. Find the action key

List the app's read actions with `GET /v2/actions`, using the app `id` from the previous step. The action key identifies which trigger the inbox subscribes to. Scan the response for the trigger you want by its human-readable `title`, then read its `key`. That `key` is your `action_key`.

<CodeGroup>
  ```bash cURL theme={null}
  curl -s "https://api.zapier.com/v2/actions?app=$APP_ID&action_type=READ" \
    -H "Authorization: Bearer $TOKEN" | python3 -m json.tool
  ```

  ```typescript TypeScript theme={null}
  const res = await fetch(
    `https://api.zapier.com/v2/actions?app=${APP_ID}&action_type=READ`,
    { headers: { Authorization: `Bearer ${TOKEN}` } }
  );
  const { data } = await res.json();
  console.log(data.map((a) => ({ key: a.key, id: a.id, title: a.title })));
  ```

  ```python Python theme={null}
  resp = requests.get(
      "https://api.zapier.com/v2/actions",
      params={"app": APP_ID, "action_type": "READ"},
      headers={"Authorization": f"Bearer {TOKEN}"},
  )
  resp.raise_for_status()
  for action in resp.json()["data"]:
      print(action["key"], action["id"], action["title"])
  ```
</CodeGroup>

Each action has a human-readable `title`, a stable `key`, and an opaque `id`:

```json theme={null}
{
  "data": [
    {
      "id": "core:AZko0fdDdRnYyOQ3EDpQO2154JW9JK",
      "key": "channel_message",
      "type": "action",
      "action_type": "READ",
      "is_instant": true,
      "title": "New Message Posted to Channel",
      "description": "Triggers when a new message is posted to a channel."
    }
  ]
}
```

Match your trigger by `title`, then use its `key` (for example, `channel_message`) as your `action_key`. Replace `ACTION_ID` in the next steps with the `id` value from the response.

<Note>
  Each action's `id` is its action ID: the opaque, encoded value you pass as the `{action_id}` path parameter in later requests. It is distinct from `action_key`: `action_key` is the stable `key` you put in the inbox `subscription` (Step 2), while the action ID is this opaque `id` you put in `/v2/actions/{action_id}/...` URLs. It is not something you can construct. It is also not stable: the same action returns a different `id` on each `GET /v2/actions` call, though any `id` you capture stays valid for later requests. Capture it now. You need it to list input fields in Step 4 (`POST /v2/actions/{action_id}/inputs`), and the optional schema preview below uses it too.
</Note>

### 3. Find the connection ID

List the user's authentications for your app. Replace `APP_ID` in the request with the app `id` from the app key step. Each authentication represents a connected app account, with a UUID `id` and an `app` field holding the app's UUID.

<CodeGroup>
  ```bash cURL theme={null}
  curl -s "https://api.zapier.com/v2/authentications?app=$APP_ID" \
    -H "Authorization: Bearer $TOKEN" | python3 -m json.tool
  ```

  ```typescript TypeScript theme={null}
  const APP_ID = "81f613aa-c98a-4383-a5fc-195e68647217"; // app id from the app key step
  const res = await fetch(`https://api.zapier.com/v2/authentications?app=${APP_ID}`, {
    headers: { Authorization: `Bearer ${TOKEN}` },
  });
  const { data } = await res.json();
  const connection = data.find((auth) => !auth.is_expired);
  console.log("connection_id:", connection?.id);
  ```

  ```python Python theme={null}
  APP_ID = "81f613aa-c98a-4383-a5fc-195e68647217"  # app id from the app key step
  resp = requests.get(
      "https://api.zapier.com/v2/authentications",
      params={"app": APP_ID},
      headers={"Authorization": f"Bearer {TOKEN}"},
  )
  resp.raise_for_status()
  connection = next(a for a in resp.json()["data"] if not a["is_expired"])
  print("connection_id:", connection["id"])
  ```
</CodeGroup>

```json theme={null}
{
  "data": [
    {
      "type": "authentication",
      "id": "019487c8-6b2a-7c1e-9f3d-2a1b0c4d5e6f",
      "app": "81f613aa-c98a-4383-a5fc-195e68647217",
      "is_expired": false,
      "title": "Slack some.user@example.com"
    }
  ]
}
```

Match the authentication whose `app` equals the app `id` from the previous step, then use its `id` as your `connection_id`. Replace `CONNECTION_ID` in the next steps with this `id`.

<Note>
  The authentications endpoint requires the app's UUID (`id`), not the `app_key` string. Use the app `id` from the app key step here, and `app_key` in the subscription request in Step 2.
</Note>

### 4. Find the trigger's input fields

A trigger accepts input fields, for example, which channel to monitor. List them with `POST /v2/actions/{action_id}/inputs`, using the action ID and `connection_id` from the previous steps. Pass empty `inputs` (`{}`) to get the initial set of fields.

<CodeGroup>
  ```bash cURL theme={null}
  curl -s -X POST "https://api.zapier.com/v2/actions/$ACTION_ID/inputs" \
    -H "Authorization: Bearer $TOKEN" \
    -H "Content-Type: application/json" \
    -d "{
      \"data\": {
        \"authentication\": \"$CONNECTION_ID\",
        \"inputs\": {}
      }
    }" | python3 -m json.tool
  ```

  ```typescript TypeScript theme={null}
  const ACTION_ID = "core:AZko0fdDdRnYyOQ3EDpQO2154JW9JK"; // id from the action key step
  const CONNECTION_ID = "019487c8-6b2a-7c1e-9f3d-2a1b0c4d5e6f"; // id from the connection step
  const res = await fetch(
    `https://api.zapier.com/v2/actions/${ACTION_ID}/inputs`,
    {
      method: "POST",
      headers: {
        Authorization: `Bearer ${TOKEN}`,
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        data: { authentication: CONNECTION_ID, inputs: {} },
      }),
    }
  );
  const { data } = await res.json();
  console.log(
    data.map((f) => ({ id: f.id, title: f.title, is_required: f.is_required }))
  );
  ```

  ```python Python theme={null}
  ACTION_ID = "core:AZko0fdDdRnYyOQ3EDpQO2154JW9JK"  # id from the action key step
  CONNECTION_ID = "019487c8-6b2a-7c1e-9f3d-2a1b0c4d5e6f"  # id from the connection step
  resp = requests.post(
      f"https://api.zapier.com/v2/actions/{ACTION_ID}/inputs",
      json={"data": {"authentication": CONNECTION_ID, "inputs": {}}},
      headers={"Authorization": f"Bearer {TOKEN}"},
  )
  resp.raise_for_status()
  for field in resp.json()["data"]:
      print(field["id"], field["is_required"], field["title"])
  ```
</CodeGroup>

Each `input_field` has an `id` (the key you use in `inputs`), a `title`, and `is_required`:

```json theme={null}
{
  "data": [
    {
      "type": "input_field",
      "id": "channel",
      "default_value": "",
      "depends_on": [],
      "description": "Only channels and MPDMs you are a member of will appear in this list.",
      "format": "SELECT",
      "invalidates_input_fields": false,
      "is_required": true,
      "placeholder": "",
      "title": "Channel",
      "value_type": "STRING"
    }
  ]
}
```

Build your `inputs` object from the field `id`s and the values you want, for example, `{"channel": "C0123456789"}`.

<Note>
  Some fields are dynamic: they appear only after a connection or another field is set (check each field's `depends_on`). To reveal them, send the values you have back as `inputs` and call again. The response may also include `info_field` entries (display-only) and `fieldset` entries (groups of related fields).
</Note>

Carry these four values into Step 2:

| Inbox subscription field | Source                                              | Example                                |
| ------------------------ | --------------------------------------------------- | -------------------------------------- |
| `app_key`                | `GET /v2/apps` → `key`                              | `SlackCLIAPI`                          |
| `action_key`             | `GET /v2/actions` → `key`                           | `channel_message`                      |
| `connection_id`          | `GET /v2/authentications` → `id`                    | `019487c8-6b2a-7c1e-9f3d-2a1b0c4d5e6f` |
| `inputs`                 | `POST /v2/actions/{action_id}/inputs` → field `id`s | `{"channel": "C0123456789"}`           |

<Accordion title="Optional: Preview the data your inbox will produce">
  Before you create the inbox, you can preview the fields its messages will contain. The trigger's declared output schema covers the core fields that land in each message's `payload` (real payloads can include additional fields, as the note below explains). It is available from the [Workflow API](/powered-by-zapier/api-reference/actions/get-output-fields) using the action ID you captured in Step 1. No inbox or live data required.

  Post the `action_id` to the outputs endpoint with empty `inputs` to get the declared schema. Passing the `connection_id` and the actual `inputs` you'll use (from the steps above) sharpens the result. Both keys are required: `authentication` may be `null` and `inputs` may be `{}`, but neither key can be omitted.

  <CodeGroup>
    ```bash cURL theme={null}
    curl -s -X POST "https://api.zapier.com/v2/actions/$ACTION_ID/outputs" \
      -H "Authorization: Bearer $TOKEN" \
      -H "Content-Type: application/json" \
      -d "{
        \"data\": {
          \"authentication\": \"$CONNECTION_ID\",
          \"inputs\": {},
          \"fetch_live_samples\": false
        }
      }" | python3 -m json.tool
    ```

    ```typescript TypeScript theme={null}
    const ACTION_ID = "core:AZko0fdDdRnYyOQ3EDpQO2154JW9JK"; // id from the action key step
    const CONNECTION_ID = "019487c8-6b2a-7c1e-9f3d-2a1b0c4d5e6f"; // id from the connection step
    const res = await fetch(
      `https://api.zapier.com/v2/actions/${ACTION_ID}/outputs`,
      {
        method: "POST",
        headers: {
          Authorization: `Bearer ${TOKEN}`,
          "Content-Type": "application/json",
        },
        body: JSON.stringify({
          data: {
            authentication: CONNECTION_ID,
            inputs: {},
            fetch_live_samples: false,
          },
        }),
      }
    );
    const { data } = await res.json();
    console.log(data.map((f) => ({ id: f.id, title: f.title, sample: f.sample })));
    ```

    ```python Python theme={null}
    ACTION_ID = "core:AZko0fdDdRnYyOQ3EDpQO2154JW9JK"  # id from the action key step
    CONNECTION_ID = "019487c8-6b2a-7c1e-9f3d-2a1b0c4d5e6f"  # id from the connection step
    resp = requests.post(
        f"https://api.zapier.com/v2/actions/{ACTION_ID}/outputs",
        json={
            "data": {
                "authentication": CONNECTION_ID,
                "inputs": {},
                "fetch_live_samples": False,
            }
        },
        headers={"Authorization": f"Bearer {TOKEN}"},
    )
    resp.raise_for_status()
    for field in resp.json()["data"]:
        print(field["id"], "-", field["title"])
    ```
  </CodeGroup>

  Each `id` is a field that will appear in your message `payload`; `sample` is illustrative:

  ```json theme={null}
  {
    "data": [
      {
        "type": "output_field",
        "id": "text",
        "title": "Text",
        "sample": "Hello world"
      },
      {
        "type": "output_field",
        "id": "user__name",
        "title": "User Name",
        "sample": "xavdid"
      }
    ]
  }
  ```

  <Note>
    This is the trigger's declared schema, not fields inferred from this inbox's received messages. Set `fetch_live_samples` to `true` to populate `sample` values with real data from the app through your connection. A real-data fetch needs a valid `authentication` and the `inputs` the trigger reads from (for example, a `channel`); without them it usually returns nothing and falls back to the declared samples. Setting `fetch_live_samples` to `true` always performs the fetch, even when empty `inputs` mean it returns nothing useful, and each fetch adds noticeable latency and counts against a rate limit. It is not available for write actions. Live results may also include fields beyond the declared schema.
  </Note>
</Accordion>

***

## Step 2: Create the inbox

Replace `YOUR_APP_KEY`, `YOUR_ACTION_KEY`, `YOUR_CONNECTION_ID`, and `YOUR_INPUT_KEY`/`YOUR_INPUT_VALUE` with the values from Step 1 and your prerequisites.

<CodeGroup>
  ```bash cURL theme={null}
  curl -s -X POST "https://api.zapier.com/trigger-inbox/v1/inboxes" \
    -H "Authorization: Bearer $TOKEN" \
    -H "Content-Type: application/json" \
    -d '{
      "name": "my-slack-inbox",
      "subscription": {
        "app_key": "YOUR_APP_KEY",
        "action_key": "YOUR_ACTION_KEY",
        "connection_id": "YOUR_CONNECTION_ID",
        "inputs": {
          "YOUR_INPUT_KEY": "YOUR_INPUT_VALUE"
        }
      }
    }' | python3 -m json.tool

  export INBOX_ID="<id from response>"
  ```

  ```typescript TypeScript theme={null}
  const res = await fetch(
    "https://api.zapier.com/trigger-inbox/v1/inboxes",
    {
      method: "POST",
      headers: {
        Authorization: `Bearer ${TOKEN}`,
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        name: "my-slack-inbox",
        subscription: {
          app_key: "YOUR_APP_KEY",
          action_key: "YOUR_ACTION_KEY",
          connection_id: "YOUR_CONNECTION_ID",
          inputs: { YOUR_INPUT_KEY: "YOUR_INPUT_VALUE" },
        },
      }),
    }
  );
  const inbox = await res.json();
  const INBOX_ID = inbox.id;
  console.log("Inbox ID:", INBOX_ID);
  ```

  ```python Python theme={null}
  response = requests.post(
      "https://api.zapier.com/trigger-inbox/v1/inboxes",
      json={
          "name": "my-slack-inbox",
          "subscription": {
              "app_key": "YOUR_APP_KEY",
              "action_key": "YOUR_ACTION_KEY",
              "connection_id": "YOUR_CONNECTION_ID",
              "inputs": {"YOUR_INPUT_KEY": "YOUR_INPUT_VALUE"},
          },
      },
      headers={"Authorization": f"Bearer {TOKEN}"},
  )
  response.raise_for_status()
  INBOX_ID = response.json()["id"]
  print("Inbox ID:", INBOX_ID)
  ```
</CodeGroup>

***

## Step 3: Wait for active

Poll until the status is `active`. Initialization typically takes a few seconds. Go to [inbox states](/white-label/trigger-inbox/what-is-trigger-inbox-api#inbox-states) for an overview of all status transitions.

<CodeGroup>
  ```bash cURL theme={null}
  DELAY=5
  while true; do
    STATUS=$(curl -s "https://api.zapier.com/trigger-inbox/v1/inboxes/$INBOX_ID" \
      -H "Authorization: Bearer $TOKEN" \
      | python3 -c "import sys,json; print(json.load(sys.stdin)['status'])")
    echo "Status: $STATUS"
    [ "$STATUS" = "active" ] && break
    if [ "$STATUS" = "initialization_failure" ]; then
      echo "Inbox setup failed. Check paused_reason, delete this inbox, and try again."
      exit 1
    fi
    sleep $DELAY
    DELAY=$(( DELAY < 60 ? DELAY * 2 : 60 ))
  done
  ```

  ```typescript TypeScript theme={null}
  let status: string;
  do {
    await new Promise((resolve) => setTimeout(resolve, 5000));
    const res = await fetch(
      `https://api.zapier.com/trigger-inbox/v1/inboxes/${INBOX_ID}`,
      { headers: { Authorization: `Bearer ${TOKEN}` } }
    );
    const inbox = await res.json();
    status = inbox.status;
    console.log("Status:", status);
    if (status === "initialization_failure") {
      throw new Error("Inbox setup failed. Delete this inbox and try again.");
    }
  } while (status !== "active");
  ```

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

  while True:
      resp = requests.get(
          f"https://api.zapier.com/trigger-inbox/v1/inboxes/{INBOX_ID}",
          headers={"Authorization": f"Bearer {TOKEN}"},
      )
      status = resp.json()["status"]
      print("Status:", status)
      if status == "active":
          break
      if status == "initialization_failure":
          raise RuntimeError("Inbox setup failed. Delete this inbox and try again.")
      time.sleep(5)
  ```
</CodeGroup>

***

## Step 4: Send an event from the connected app

Perform an action in the connected app to produce a trigger event. For example, if you subscribed to Slack new messages, post a message to the monitored channel.

The event will appear in the inbox within a few seconds to a few minutes, depending on the [trigger type](/integrations/build/trigger).

***

## Step 5: Lease messages

<CodeGroup>
  ```bash cURL theme={null}
  curl -s -X POST "https://api.zapier.com/trigger-inbox/v1/inboxes/$INBOX_ID/messages/lease" \
    -H "Authorization: Bearer $TOKEN" \
    -H "Content-Type: application/json" \
    -d '{"lease_seconds": 300, "lease_limit": 10}' | python3 -m json.tool

  export LEASE_ID="<lease_id from response>"
  ```

  ```typescript TypeScript theme={null}
  const res = await fetch(
    `https://api.zapier.com/trigger-inbox/v1/inboxes/${INBOX_ID}/messages/lease`,
    {
      method: "POST",
      headers: {
        Authorization: `Bearer ${TOKEN}`,
        "Content-Type": "application/json",
      },
      body: JSON.stringify({ lease_seconds: 300, lease_limit: 10 }),
    }
  );
  const data = await res.json();
  const LEASE_ID = data.lease_id;
  console.log("Lease ID:", LEASE_ID);
  console.log("Messages:", data.results);
  ```

  ```python Python theme={null}
  response = requests.post(
      f"https://api.zapier.com/trigger-inbox/v1/inboxes/{INBOX_ID}/messages/lease",
      json={"lease_seconds": 300, "lease_limit": 10},
      headers={"Authorization": f"Bearer {TOKEN}"},
  )
  response.raise_for_status()
  data = response.json()
  LEASE_ID = data["lease_id"]
  print("Lease ID:", LEASE_ID)
  print("Messages:", data["results"])
  ```
</CodeGroup>

Each message in `results[]` contains the trigger payload in `payload`. If `possible_duplicate_data` is `true`, deduplication was not possible and your processing logic must be idempotent.

***

## Step 6: Acknowledge messages

<CodeGroup>
  ```bash cURL theme={null}
  curl -s -X POST "https://api.zapier.com/trigger-inbox/v1/inboxes/$INBOX_ID/messages/ack" \
    -H "Authorization: Bearer $TOKEN" \
    -H "Content-Type: application/json" \
    -d "{\"lease_id\": \"$LEASE_ID\"}" | python3 -m json.tool
  ```

  ```typescript TypeScript theme={null}
  await fetch(
    `https://api.zapier.com/trigger-inbox/v1/inboxes/${INBOX_ID}/messages/ack`,
    {
      method: "POST",
      headers: {
        Authorization: `Bearer ${TOKEN}`,
        "Content-Type": "application/json",
      },
      body: JSON.stringify({ lease_id: LEASE_ID }),
    }
  );
  ```

  ```python Python theme={null}
  requests.post(
      f"https://api.zapier.com/trigger-inbox/v1/inboxes/{INBOX_ID}/messages/ack",
      json={"lease_id": LEASE_ID},
      headers={"Authorization": f"Bearer {TOKEN}"},
  ).raise_for_status()
  ```
</CodeGroup>

If processing fails before you acknowledge, let the lease expire. Messages return to the queue automatically and become available to lease again.

***

## Complete script

Complete the [Before you begin](#before-you-begin) steps first, then set your variables and run the script:

```bash theme={null}
#!/usr/bin/env bash
# Trigger Inbox — app script
# Prerequisites: TOKEN, APP_KEY, ACTION_KEY, CONNECTION_ID, and INPUTS set
# Usage: export TOKEN="..." APP_KEY="..." ACTION_KEY="..." CONNECTION_ID="..." && bash real-app.sh

set -euo pipefail

BASE_URL="https://api.zapier.com/trigger-inbox/v1"

# Replace these with your values from Step 1
APP_KEY="${APP_KEY}"
ACTION_KEY="${ACTION_KEY}"
CONNECTION_ID="${CONNECTION_ID}"
# Replace with the required inputs for your trigger (key/value pairs)
INPUTS='{"YOUR_INPUT_KEY": "YOUR_INPUT_VALUE"}'

# 1. Create the inbox
echo "Creating inbox..."
INBOX_ID=$(curl -s -X POST "$BASE_URL/inboxes" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d "{
    \"name\": \"real-app-inbox-$(date +%s)\",
    \"subscription\": {
      \"app_key\": \"$APP_KEY\",
      \"action_key\": \"$ACTION_KEY\",
      \"connection_id\": \"$CONNECTION_ID\",
      \"inputs\": $INPUTS
    }
  }" | python3 -c "import sys,json; print(json.load(sys.stdin)['id'])")
echo "Inbox ID: $INBOX_ID"

# 2. Wait for active
echo "Waiting for inbox to become active..."
DELAY=5
while true; do
  STATUS=$(curl -s "$BASE_URL/inboxes/$INBOX_ID" \
    -H "Authorization: Bearer $TOKEN" \
    | python3 -c "import sys,json; print(json.load(sys.stdin)['status'])")
  echo "  Status: $STATUS"
  [ "$STATUS" = "active" ] && break
  if [ "$STATUS" = "initialization_failure" ]; then
    echo "Inbox setup failed. Check paused_reason, delete this inbox, and try again."
    exit 1
  fi
  sleep $DELAY
  DELAY=$(( DELAY < 60 ? DELAY * 2 : 60 ))
done

# 3. Send an event from the connected app, then press Enter to continue
echo ""
echo "Inbox is active. Send an event from your connected app, then press Enter."
read -r

# 4. Lease messages
echo "Leasing messages..."
DELAY=5
while true; do
  LEASE_RESPONSE=$(curl -s -X POST "$BASE_URL/inboxes/$INBOX_ID/messages/lease" \
    -H "Authorization: Bearer $TOKEN" \
    -H "Content-Type: application/json" \
    -d '{"lease_seconds": 300, "lease_limit": 10}')

  LEASE_ID=$(echo "$LEASE_RESPONSE" \
    | python3 -c "import sys,json; print(json.load(sys.stdin).get('lease_id') or '')" 2>/dev/null)

  if [ -n "$LEASE_ID" ]; then
    echo "Messages leased:"
    echo "$LEASE_RESPONSE" | python3 -m json.tool
    break
  fi

  echo "  No messages yet, retrying in ${DELAY}s..."
  sleep $DELAY
  DELAY=$(( DELAY < 60 ? DELAY * 2 : 60 ))
done

# 5. Acknowledge
echo "Acknowledging messages..."
curl -s -X POST "$BASE_URL/inboxes/$INBOX_ID/messages/ack" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d "{\"lease_id\": \"$LEASE_ID\"}" | python3 -m json.tool

echo ""
echo "Done."
```

***

## Next steps

* [Consuming messages](/white-label/trigger-inbox/consuming-messages) for partial acknowledgment, release, backoff strategy, and quarantine handling.
* [Manage your inbox](/white-label/trigger-inbox/manage-your-inbox) for pause, resume, update, and delete operations.
* [Trigger Inbox API reference](/white-label/api-reference/inboxes/list-inboxes) for full endpoint documentation.
