Webhooks
Last updated: 2026-03-07
Setting Up Webhooks#
Webhooks notify your server when events occur in your Altrixy account. Set them up via the API or the dashboard.
Via Dashboard:
1. Go to Settings > API > Webhooks
2. Click Add Webhook
3. Enter your endpoint URL (must be HTTPS)
4. Select which events to subscribe to
5. Click Save
Via API:
curl -X POST \
-H "X-API-Key: altx_sk_your_key_here" \
-H "Content-Type: application/json" \
-d '{
"url": "https://your-server.com/webhooks/altrixy",
"events": ["portfolio.published", "portfolio.updated"],
"secret": "whsec_your_signing_secret"
}' \
https://api.altrixy.com/v1/webhooksResponse (201 Created):
{
"id": "whk_abc123",
"url": "https://your-server.com/webhooks/altrixy",
"events": ["portfolio.published", "portfolio.updated"],
"status": "active",
"created_at": "2026-03-15T10:00:00Z"
}Available Events#
Subscribe to any combination of the following events:
| Event | Trigger |
|---|---|
portfolio.created | A new portfolio is created |
portfolio.updated | A portfolio's content or settings are modified |
portfolio.published | A portfolio is published |
portfolio.unpublished | A published portfolio is taken offline |
portfolio.deleted | A portfolio is permanently deleted |
portfolio.generation.completed | AI generation finishes for a portfolio |
portfolio.generation.failed | AI generation fails |
interview.session.completed | An interview session is finished and scored |
voice.clone.ready | A voice clone finishes training |
voice.audio.generated | An audio clip is generated |
linkedin.post.published | A LinkedIn post is published through Altrixy |
linkedin.post.scheduled | A LinkedIn post is scheduled |
Use * to subscribe to all events.
Webhook Payload Format#
Every webhook delivery sends a POST request with the following structure:
{
"id": "evt_abc123",
"type": "portfolio.published",
"created_at": "2026-03-15T12:00:00Z",
"data": {
"id": "ptf_abc123",
"title": "Redesigning Checkout at Stripe",
"status": "published",
"url": "https://altrixy.com/p/checkout-redesign-stripe",
"quality_score": 87
}
}HTTP headers included:
| Header | Description |
|---|---|
Content-Type | application/json |
X-Altrixy-Event | The event type (e.g., portfolio.published) |
X-Altrixy-Delivery | Unique delivery ID for deduplication |
X-Altrixy-Signature | HMAC-SHA256 signature for verification |
X-Altrixy-Timestamp | Unix timestamp of the delivery |
Signature Verification#
Every webhook delivery is signed with your webhook secret using HMAC-SHA256. Always verify signatures to ensure the request came from Altrixy.
Verification process:
- Extract the
X-Altrixy-SignatureandX-Altrixy-Timestampheaders - Construct the signed payload:
{timestamp}.{request_body} - Compute HMAC-SHA256 using your webhook secret
- Compare the computed signature with the header value
Node.js example:
const crypto = require('crypto');
function verifyWebhook(payload, signature, timestamp, secret) {
const signedPayload = `${timestamp}.${payload}`;
const expected = crypto
.createHmac('sha256', secret)
.update(signedPayload)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected)
);
}Python example:
import hmac
import hashlib
def verify_webhook(payload: str, signature: str, timestamp: str, secret: str) -> bool:
signed_payload = f"{timestamp}.{payload}"
expected = hmac.new(
secret.encode(),
signed_payload.encode(),
hashlib.sha256
).hexdigest()
return hmac.compare_digest(signature, expected)Always use timing-safe comparison functions to prevent timing attacks. Reject deliveries where the timestamp is more than 5 minutes old to prevent replay attacks.
Retry Policy#
If your endpoint returns a non-2xx status code or times out, Altrixy retries with exponential backoff:
| Attempt | Delay | Total elapsed |
|---|---|---|
| 1st retry | 1 minute | 1 minute |
| 2nd retry | 5 minutes | 6 minutes |
| 3rd retry | 30 minutes | 36 minutes |
| 4th retry | 2 hours | ~2.5 hours |
| 5th retry | 8 hours | ~10.5 hours |
After 5 failed retries, the delivery is marked as failed and no further attempts are made.
Timeout: Your endpoint must respond within 30 seconds. If you need to do heavy processing, respond with a 200 immediately and handle the event asynchronously.
Idempotency: Use the X-Altrixy-Delivery header to deduplicate events. Multiple retries for the same delivery will have the same delivery ID.
Testing Webhooks#
Test your webhook integration before going live:
1. Dashboard Test Ping
Go to Settings > API > Webhooks, find your webhook, and click Send Test. This sends a ping event to your endpoint:
{
"id": "evt_test_123",
"type": "ping",
"created_at": "2026-03-15T12:00:00Z",
"data": {
"message": "Webhook test from Altrixy"
}
}2. CLI Testing
Use the Altrixy CLI to forward webhooks to your local development server:
npx altrixy webhooks listen --forward http://localhost:3000/webhooksThis creates a temporary tunnel and registers a webhook that forwards events to your local server.
3. Event Log
View all webhook deliveries in Settings > API > Webhooks > Delivery Log. Each entry shows the event type, response status, response time, and request/response bodies.
Troubleshooting#
Common webhook issues and solutions:
Webhook not receiving events
- Verify the endpoint URL is correct and uses HTTPS
- Check that the webhook status is "active" in the dashboard
- Ensure your server's firewall allows incoming requests from Altrixy's IP range: 52.14.0.0/16
- Confirm you have subscribed to the correct events
Signature verification failing
- Ensure you are using the raw request body (not a parsed/re-serialized version)
- Verify you are using the correct webhook secret (not your API key)
- Check that you are constructing the signed payload correctly: {timestamp}.{body}
Receiving duplicate events
- Implement idempotency using the X-Altrixy-Delivery header
- Store processed delivery IDs and skip duplicates
Events arriving late
- Webhooks are delivered in near-real-time but are not guaranteed to be instantaneous
- Events may arrive out of order during retries
- Use the created_at timestamp in the payload for ordering, not delivery time
Monitor your webhook endpoint's response time. If it consistently takes more than 10 seconds, consider processing events asynchronously to avoid timeouts.