Skip to main content

Create a trigger inbox for an app

This tutorial covers the Trigger Inbox API connected to an app using the user’s app connection. It follows the same queue model as Trigger Inbox onboarding, adding connection setup before inbox creation.

Before you begin

  • An access token set as $TOKEN. Token exchange covers how to get one.
  • A connected app account for the user. Connection flow covers setup. You will gather everything else in Step 1.

Step 1: Gather your subscription values

Creating an inbox for an app needs three values from the Powered by Zapier API, 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.
curl -s "https://api.zapier.com/v2/apps?query=Slack" \
  -H "Authorization: Bearer $TOKEN" | python3 -m json.tool
Each app has a versionless key and a UUID id:
{
  "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.
curl -s "https://api.zapier.com/v2/actions?app=$APP_ID&action_type=READ" \
  -H "Authorization: Bearer $TOKEN" | python3 -m json.tool
Each action has a human-readable title, a stable key, and an opaque id:
{
  "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.
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.

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.
curl -s "https://api.zapier.com/v2/authentications?app=$APP_ID" \
  -H "Authorization: Bearer $TOKEN" | python3 -m json.tool
{
  "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.
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.

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.
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
Each input_field has an id (the key you use in inputs), a title, and is_required:
{
  "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 ids and the values you want, for example, {"channel": "C0123456789"}.
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).
Carry these four values into Step 2:
Inbox subscription fieldSourceExample
app_keyGET /v2/appskeySlackCLIAPI
action_keyGET /v2/actionskeychannel_message
connection_idGET /v2/authenticationsid019487c8-6b2a-7c1e-9f3d-2a1b0c4d5e6f
inputsPOST /v2/actions/{action_id}/inputs → field ids{"channel": "C0123456789"}
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 Powered by Zapier API 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.
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
Each id is a field that will appear in your message payload; sample is illustrative:
{
  "data": [
    {
      "type": "output_field",
      "id": "text",
      "title": "Text",
      "sample": "Hello world"
    },
    {
      "type": "output_field",
      "id": "user__name",
      "title": "User Name",
      "sample": "xavdid"
    }
  ]
}
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.

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.
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>"

Step 3: Wait for active

Poll until the status is active. Initialization typically takes a few seconds. Go to inbox states for an overview of all status transitions.
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

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.

Step 5: Lease messages

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>"
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

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
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 steps first, then set your variables and run the script:
#!/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