Webhooks
Receive real-time notifications when orders are created and fulfilled
ACP webhooks notify your systems when order events occur. When a checkout completes, a signed HTTP POST is sent to your configured webhook URL with the order details.
Setting Up Webhooks
- Log into build.chipp.ai and navigate to your app
- Go to Publish > Catalog
- Find your API key in the API Keys section
- Click to configure the webhook URL and secret
Each API key has its own webhook configuration. You can set:
- Webhook URL — The HTTPS endpoint that receives events
- Webhook Secret — A secret string used to sign payloads for verification
Webhook URLs must be publicly accessible HTTPS endpoints. The webhook secret is used to generate HMAC-SHA256 signatures so you can verify that requests are authentic.
Event Types
| Event | Trigger |
|---|---|
order.created | An order was created from a completed checkout (manual fulfillment) |
order.fulfilled | An order was automatically fulfilled (tool execution succeeded) |
When a checkout with automatic fulfillment products completes, the event type will be order.fulfilled. For manual fulfillment products, the event type will be order.created.
Webhook Payload
{
"event": "order.fulfilled",
"order_id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"checkout_session_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"application_id": "d4e5f6a7-b8c9-0123-def0-123456789abc",
"status": "fulfilled",
"fulfillment_result": [
{ "toolName": "analyze_data", "success": true, "output": "Analysis complete" }
],
"total": {
"amount": 2500,
"currency": "usd"
},
"created_at": "2025-01-15T12:05:00.000Z"
}Payload Fields
| Field | Type | Description |
|---|---|---|
event | string | The event type (see table above) |
order_id | string (UUID) | Unique order identifier |
checkout_session_id | string (UUID) | The checkout session that created this order |
application_id | string (UUID) | The application this order belongs to |
status | string | Current order status |
fulfillment_result | array or null | Results from automatic fulfillment (tool execution) |
total.amount | integer | Order total in currency minor units |
total.currency | string | ISO currency code |
created_at | string (ISO 8601) | When the order was created |
Webhook Headers
Every webhook request includes these headers:
| Header | Description |
|---|---|
Content-Type | application/json |
X-ACP-Event | The event type (e.g., order.fulfilled) |
X-ACP-Timestamp | Unix timestamp (seconds) when the webhook was sent |
X-ACP-Signature | HMAC-SHA256 signature for payload verification |
Signature Verification
Verify webhook authenticity by computing the HMAC-SHA256 signature and comparing it to the X-ACP-Signature header.
The signature is computed over the string {timestamp}.{body}, where {timestamp} is the X-ACP-Timestamp header value and {body} is the raw JSON request body.
Node.js Example
import crypto from "crypto";
function verifyWebhook(req, webhookSecret) {
const signature = req.headers["x-acp-signature"];
const timestamp = req.headers["x-acp-timestamp"];
const body = req.body; // raw string, not parsed JSON
const expected = crypto
.createHmac("sha256", webhookSecret)
.update(`${timestamp}.${body}`)
.digest("hex");
return crypto.timingSafeEqual(
Buffer.from(signature, "hex"),
Buffer.from(expected, "hex")
);
}Python Example
import hmac
import hashlib
def verify_webhook(headers, body, webhook_secret):
signature = headers["X-ACP-Signature"]
timestamp = headers["X-ACP-Timestamp"]
expected = hmac.new(
webhook_secret.encode(),
f"{timestamp}.{body}".encode(),
hashlib.sha256
).hexdigest()
return hmac.compare_digest(signature, expected)Always use constant-time comparison (like timingSafeEqual or hmac.compare_digest) when verifying signatures to prevent timing attacks.
Delivery Behavior
- Timeout: Each delivery attempt has a 10-second timeout
- Retries: Up to 3 delivery attempts (1 initial + 2 retries) with exponential backoff (1s, 2s delays)
- Success: A
2xxHTTP response from your endpoint is considered successful delivery - Failure: After all retries fail, the error is recorded on the order. You can check the order status via the API
Best Practices
- Respond quickly: Return a
200response as soon as you receive the webhook. Process the payload asynchronously if needed. - Handle duplicates: Webhooks may be delivered more than once. Use the
order_idto deduplicate. - Verify signatures: Always verify the
X-ACP-Signatureheader before processing the payload. - Use HTTPS: Webhook URLs must use HTTPS to protect payload contents in transit.