Webhooks API

Subscribe to events and receive real-time notifications via webhooks.

The Webhooks API lets you subscribe to events programmatically. Use it for Zapier-style instant triggers or custom integrations that need real-time notifications.

Note: The Webhooks API requires a Hobby plan or above with active billing.

Endpoints

EndpointMethodDescription
/api/v1/webhooks/subscribePOSTCreate webhook subscription
/api/v1/webhooks/subscribe/[id]DELETERemove subscription
/api/v1/chatbotsGETList agents (for subscription)

Create Subscription

POST /api/v1/webhooks/subscribe

Request Body

{
  "event": "form_submission",
  "targetUrl": "https://your-server.com/webhook",
  "chatbotId": "123e4567-e89b-12d3-a456-426614174000"
}

Parameters

FieldTypeRequiredDescription
eventstringYeslead_created, form_submission, custom_form_submitted, conversation_started, conversation_ended, or escalation_created
targetUrlstringYesURL to receive events — HTTPS required in production, HTTP also accepted outside production
chatbotIdstring (UUID)YesAgent to subscribe to

Response

Success (201):

{
  "id": "subscription-uuid"
}

Example

curl -X POST 'https://your-domain.com/api/v1/webhooks/subscribe' \
  -H 'Authorization: Bearer YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{
    "event": "form_submission",
    "targetUrl": "https://hooks.zapier.com/hooks/catch/123456/abcdef",
    "chatbotId": "123e4567-e89b-12d3-a456-426614174000"
  }'

Delete Subscription

DELETE /api/v1/webhooks/subscribe/[id]

Response

Success (200): Returns an empty response with status 200.

Example

curl -X DELETE 'https://your-domain.com/api/v1/webhooks/subscribe/sub_abc123' \
  -H 'Authorization: Bearer YOUR_API_KEY'

List Agents

Get available agents to subscribe to:

GET /api/v1/chatbots

Response

{
  "chatbots": [
    {
      "id": "123e4567-e89b-12d3-a456-426614174000",
      "name": "Support Bot",
      "url": "https://your-domain.com/chat/support-bot"
    }
  ]
}

Webhook Payload

When an event occurs, we POST to your target URL:

Form Submission Event

{
  "event": "form_submission",
  "eventId": "evt_91d0a3ce-5a9d-4b67-a454-bafb9484281a",
  "data": {
    "id": "lead-uuid",
    "chatbotId": "123e4567-e89b-12d3-a456-426614174000",
    "name": "John Smith",
    "email": "[email protected]",
    "phone": "+1234567890",
    "conversationId": "conv_abc123",
    "createdAt": "2025-01-10T12:00:00Z"
  },
  "timestamp": "2025-01-10T12:00:00Z"
}

Headers

We include these headers with each delivery:

Content-Type: application/json
User-Agent: AgentkitWebhook/2.0
X-Agentkit-Event: form_submission
X-Agentkit-Event-Id: evt_91d0a3ce-5a9d-4b67-a454-bafb9484281a
X-Agentkit-Signature: t=<unix-seconds>,v1=<hex-hmac-sha256>

Verify the signature against the exact raw UTF-8 request body before parsing JSON. See Webhook security for the signing procedure and payload catalog.

URL Requirements

Security

In production, a subscription URL must use HTTPS. Outside production, HTTP is also accepted — but only http: and https: URLs are ever allowed, and every URL must additionally pass a public-destination check on the URL itself, which rejects localhost and similar loopback hostnames, literal private and link-local addresses, and cloud metadata endpoints.

See Webhook security for the full list of destination restrictions; it is the canonical description of this rule.

Validation

The URL is checked when you create the subscription; URLs that fail the checks above are rejected with a 400 error whose message names the production requirement. The check inspects the URL as written — it does not resolve hostnames, so point your subscription at the public endpoint you actually intend to receive events. At delivery time the hostname is resolved once, every resolved address must be public, and the connection is pinned to those addresses — a hostname that resolves to a private or internal address (including one that changes between lookup and connect) is never contacted; the attempt is recorded as target_blocked.

Reliability

Response Requirements

Your endpoint should:

  • Return 2xx status within 10 seconds
  • Handle duplicate deliveries (idempotent)

Failure Handling

  • Failed deliveries are retried five times after the initial request
  • One exhausted event increments the consecutive failure streak once
  • At least 10 exhausted events spanning seven days automatically disable a subscription
  • Owners can repair and re-enable subscriptions from webhook settings
  • Delivery is at least once; use eventId for idempotency and do not assume ordering

Error Responses

StatusMessageCause
400Invalid JSON bodyMalformed JSON
400Invalid request bodyMissing/invalid fields (includes unsupported event types)
400Target URL must be a public HTTPS endpoint (localhost and private IPs are not allowed)URL fails SSRF validation
401Missing or invalid Authorization headerHeader not provided or wrong format
401Invalid API keyKey not recognized
403Webhook subscriptions require a Hobby plan or above with active billingPlan or billing status does not allow integrations
403Chatbot does not belong to this accountAgent belongs to a different account
403Subscription does not belong to this accountSubscription belongs to a different account
404Chatbot not foundInvalid agent ID
404Webhook subscription not foundInvalid subscription ID
409Webhook subscription already exists for this event and URLDuplicate subscription

Zapier Integration

These endpoints power our Zapier integration. When you connect Agentkit to Zapier:

  1. Zapier calls /api/v1/auth to verify your API key
  2. Zapier calls /api/v1/chatbots to list your agents
  3. When you enable a trigger, Zapier calls /api/v1/webhooks/subscribe
  4. Events are delivered to Zapier's webhook URL
  5. When you disable, Zapier calls DELETE on the subscription

To integrate without writing code, use Zapier directly instead of these APIs.

Set up Zapier →

Code Example

Node.js Subscription Manager

class WebhookManager {
  constructor(apiKey, baseUrl) {
    this.apiKey = apiKey;
    this.baseUrl = baseUrl;
  }

  async subscribe(chatbotId, targetUrl, event = 'form_submission') {
    const response = await fetch(`${this.baseUrl}/api/v1/webhooks/subscribe`, {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${this.apiKey}`,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({ event, targetUrl, chatbotId }),
    });

    if (!response.ok) {
      throw new Error(`Subscribe failed: ${response.status}`);
    }

    return response.json();
  }

  async unsubscribe(subscriptionId) {
    const response = await fetch(
      `${this.baseUrl}/api/v1/webhooks/subscribe/${subscriptionId}`,
      {
        method: 'DELETE',
        headers: { 'Authorization': `Bearer ${this.apiKey}` },
      },
    );

    return response.ok;
  }
}

Next Steps