Skip to main content

Trigger Inbox onboarding

This article covers the core queue model of the Trigger Inbox API: create an inbox, send a message, lease it, and acknowledge it. It uses Webhooks by Zapier as the trigger so you can run the full lifecycle without connecting an external app. Follow the steps individually to understand each part of the flow, or skip to the complete script at the bottom to run everything at once. Once you understand this pattern, the app tutorial covers connecting to apps like Slack or Gmail using the user’s app connection.

Before you begin

Node.js 18 or later, installed and available as node.

Step 1: Get an access token

The Trigger Inbox API requires an access token to authenticate your requests. For local testing, use SDK client credentials. The webhook trigger used in this guide does not require an app connection, so SDK client credentials are all you need to follow along. 1. Install the Zapier Platform CLI: If you already have the Zapier Platform CLI installed, skip to step 2.
npm install -g zapier-platform-cli
2. Log in and create credentials:
npx zapier-sdk login
npx zapier-sdk create-client-credentials
The response includes a client_id and client_secret. Copy both values. 3. Exchange your credentials for an access token:
curl -s -X POST "https://zapier.com/oauth/token" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=client_credentials" \
  -d "client_id=YOUR_CLIENT_ID" \
  -d "client_secret=YOUR_CLIENT_SECRET" \
  -d "scope=external" | python3 -m json.tool
4. Set the access_token value from the response:
export TOKEN="your-access-token-here"
SDK credentials are for testing only. For production applications that act on behalf of your users, use JWT authentication.

Step 2: Create the inbox

The webhook trigger requires two randomly generated values, a hook code and a seed, that form your unique webhook URL. 1. Generate webhook codes and create the inbox:
HOOK_CODE=$(openssl rand -hex 6)
HOOK_SEED=$(openssl rand -hex 6)
INBOX_NAME="my-webhook-inbox-$(date +%s)"

curl -s -X POST "https://api.zapier.com/trigger-inbox/api/v1/inboxes" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "'"$INBOX_NAME"'",
    "subscription": {
      "app_key": "WebHookCLIAPI",
      "action_key": "hook_v2",
      "inputs": {
        "_zap_static_hook_code": "'"$HOOK_CODE"'",
        "_zap_static_hook_seed": "'"$HOOK_SEED"'"
      }
    }
  }' | python3 -m json.tool
2. Set the inbox ID and webhook URL from the response:
INBOX_ID="<id from response>"
WEBHOOK_URL="https://hooks.zapier.com/hooks/catch/$HOOK_CODE/"

Step 3: Wait for active

A new inbox starts in initializing while Zapier sets up the trigger subscription. Do not send events or lease messages until the status is active. 1. Poll until the inbox is active:
DELAY=5
while true; do
  STATUS=$(curl -s "https://api.zapier.com/trigger-inbox/api/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 create a new one."
    exit 1
  fi
  sleep $DELAY
  DELAY=$(( DELAY < 60 ? DELAY * 2 : 60 ))
done
Go to inbox states for a full description of each status.

Step 4: Send a test event

1. Send an HTTP POST to your webhook URL:
curl -s -X POST "$WEBHOOK_URL" \
  -H "Content-Type: application/json" \
  -d '{"event": "user.signup", "user_id": 42}'
The endpoint accepts the POST immediately. The message typically becomes available to lease within a few minutes.

Step 5: Lease messages

Leasing claims a batch of messages and locks them for exclusive processing. No other consumer can claim them during the lease. If you do not acknowledge within the lease duration, the messages return to the queue automatically. 1. Poll until messages are available:
DELAY=5
while true; do
  LEASE_RESPONSE=$(curl -s -X POST "https://api.zapier.com/trigger-inbox/api/v1/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 "$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
The response includes:
FieldDescription
lease_idRequired to acknowledge or release the batch.
leased_untilWhen the lease expires. Messages return to the queue after this time if not acknowledged.
results[]The leased messages.
results[].payloadThe event data. Contains your POST body plus a querystring field that Zapier adds automatically. This field is empty ({}) if no query parameters were sent.
results[].message_attributes.lease_countHow many times this message has been leased. Use this to identify repeatedly failing messages.
results[].message_attributes.possible_duplicate_datatrue if Zapier detected a possible duplicate delivery.
inbox_attributesCurrent inbox status.
2. Set the lease_id from the response:
LEASE_ID="<lease_id from response>"
Run your processing logic against the message payload before moving to the next step.

Step 6: Acknowledge messages

Acknowledging permanently removes messages from the queue. Only acknowledge after processing succeeds. 1. Acknowledge using the lease_id from Step 5:
curl -s -X POST "https://api.zapier.com/trigger-inbox/api/v1/inboxes/$INBOX_ID/messages/ack" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"lease_id": "'"$LEASE_ID"'"}' | python3 -m json.tool
A successful response returns each message with "status": "acked".
If processing fails, do not acknowledge. Let the lease expire and the messages will return to the queue automatically. To return them immediately, use release messages.The Trigger Inbox API uses at-least-once delivery. Build idempotent processing and check the message id before acting on a payload.
To acknowledge specific messages within a lease rather than the full batch, add "message_ids": ["id1", "id2"] to the request body alongside lease_id.

Step 7: Confirm the inbox is empty

After acknowledging, confirm there are no remaining messages. 1. Attempt to lease with a limit of 1:
curl -s -X POST "https://api.zapier.com/trigger-inbox/api/v1/inboxes/$INBOX_ID/messages/lease" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"lease_limit": 1}' | python3 -m json.tool
An empty inbox returns "lease_id": null and "results": [].

Step 8: Delete the test inbox

In production, inboxes are long-lived. Create them once and run the lease-process-acknowledge cycle continuously. Delete an inbox only when decommissioning an integration entirely. For this tutorial, delete the test inbox to cancel the trigger subscription. 1. Delete the inbox:
curl -s -X DELETE "https://api.zapier.com/trigger-inbox/api/v1/inboxes/$INBOX_ID" \
  -H "Authorization: Bearer $TOKEN" | python3 -m json.tool
Deletion is partially asynchronous. If you named the inbox, the name is freed immediately, so you can reuse it as soon as your DELETE call returns. The inbox status changes to deleting until Zapier finishes removing the inbox and any remaining messages on the backend, but you can treat the inbox as effectively gone as soon as you get a 202 response.

Complete script

Complete Step 1 first to get your access token, then set TOKEN and run the script:
#!/usr/bin/env bash
# Trigger Inbox onboarding — complete script
# Prerequisites: TOKEN environment variable set
# Usage: export TOKEN="your-access-token" && bash onboarding.sh

set -euo pipefail

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

# 1. Create the inbox
HOOK_CODE=$(openssl rand -hex 6)
HOOK_SEED=$(openssl rand -hex 6)
INBOX_NAME="quickstart-$(date +%s)"

echo "Creating inbox..."
INBOX_ID=$(curl -s -X POST "$BASE_URL/inboxes" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d "{
    \"name\": \"$INBOX_NAME\",
    \"subscription\": {
      \"app_key\": \"WebHookCLIAPI\",
      \"action_key\": \"hook_v2\",
      \"inputs\": {
        \"_zap_static_hook_code\": \"$HOOK_CODE\",
        \"_zap_static_hook_seed\": \"$HOOK_SEED\"
      }
    }
  }" | python3 -c "import sys,json; print(json.load(sys.stdin)['id'])")
echo "Inbox ID: $INBOX_ID"
echo "Webhook URL: $HOOKS_URL/hooks/catch/$HOOK_CODE/"

# 2. Wait for active (typically a few seconds)
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 create a new one."
    exit 1
  fi
  sleep $DELAY
  DELAY=$(( DELAY < 60 ? DELAY * 2 : 60 ))
done

# 3. Send a test event
echo "Sending test event..."
curl -s -X POST "$HOOKS_URL/hooks/catch/$HOOK_CODE/" \
  -H "Content-Type: application/json" \
  -d '{"event": "user.signup", "user_id": 42}'
echo ""
echo "Event sent. Waiting for it to become available to lease..."

# 4. Lease 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

# 6. Confirm inbox is empty
echo "Confirming inbox is empty..."
curl -s -X POST "$BASE_URL/inboxes/$INBOX_ID/messages/lease" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"lease_limit": 1}' | python3 -m json.tool

# 7. Delete the test inbox
# In production, skip this step. Inboxes are long-lived infrastructure.
echo "Deleting test inbox..."
curl -s -X DELETE "$BASE_URL/inboxes/$INBOX_ID" \
  -H "Authorization: Bearer $TOKEN" | python3 -m json.tool

echo ""
echo "Quickstart complete."

Next steps