Webhooks
Receive real-time notifications when events occur in your Canvelete (opens in a new tab) account.
Overview
Webhooks allow your application to receive HTTP POST requests when specific events happen, such as:
- Render job completed
- Design created or updated
- Export finished
- Subscription changed
Setup
Configure Webhook URL
- Go to Settings → Webhooks (opens in a new tab)
- Click Add Webhook
- Enter your endpoint URL
- Select events to subscribe to
- Save and copy the signing secret
Webhook Endpoint Requirements
Your endpoint must:
- Accept POST requests
- Return 2xx status within 30 seconds
- Handle duplicate deliveries (use
eventIdfor deduplication)
Event Types
| Event | Description |
|---|---|
render.completed | Render job finished successfully |
render.failed | Render job failed |
design.created | New design created |
design.updated | Design modified |
design.deleted | Design removed |
export.completed | Export finished |
subscription.updated | Plan changed |
Payload Format
{
"eventId": "evt_abc123",
"eventType": "render.completed",
"timestamp": "2024-01-15T10:30:00Z",
"data": {
"renderId": "render_xyz789",
"designId": "design_abc123",
"status": "completed",
"downloadUrl": "https://cdn.canvelete.com/renders/xyz789.png",
"metadata": {
"format": "png",
"width": 1080,
"height": 1080,
"fileSize": 245760
}
}
}Signature Verification
All webhooks include a signature header for verification:
X-Canvelete-Signature: sha256=abc123...Verify in Node.js
const crypto = require('crypto');
function verifyWebhook(payload, signature, secret) {
const expectedSignature = crypto
.createHmac('sha256', secret)
.update(payload)
.digest('hex');
return `sha256=${expectedSignature}` === signature;
}
// Express middleware
app.post('/webhooks/canvelete', (req, res) => {
const signature = req.headers['x-canvelete-signature'];
const payload = JSON.stringify(req.body);
if (!verifyWebhook(payload, signature, process.env.WEBHOOK_SECRET)) {
return res.status(401).send('Invalid signature');
}
// Process webhook
const { eventType, data } = req.body;
console.log(`Received ${eventType}:`, data);
res.status(200).send('OK');
});Verify in Python
import hmac
import hashlib
def verify_webhook(payload: str, signature: str, secret: str) -> bool:
expected = hmac.new(
secret.encode(),
payload.encode(),
hashlib.sha256
).hexdigest()
return f"sha256={expected}" == signature
# Flask example
@app.route('/webhooks/canvelete', methods=['POST'])
def handle_webhook():
signature = request.headers.get('X-Canvelete-Signature')
payload = request.get_data(as_text=True)
if not verify_webhook(payload, signature, WEBHOOK_SECRET):
return 'Invalid signature', 401
data = request.json
print(f"Received {data['eventType']}: {data['data']}")
return 'OK', 200Event Examples
render.completed
{
"eventId": "evt_abc123",
"eventType": "render.completed",
"timestamp": "2024-01-15T10:30:00Z",
"data": {
"renderId": "render_xyz789",
"designId": "design_abc123",
"status": "completed",
"downloadUrl": "https://cdn.canvelete.com/renders/xyz789.png",
"metadata": {
"format": "png",
"width": 1080,
"height": 1080,
"fileSize": 245760,
"renderTime": 1.2
}
}
}render.failed
{
"eventId": "evt_def456",
"eventType": "render.failed",
"timestamp": "2024-01-15T10:31:00Z",
"data": {
"renderId": "render_failed123",
"designId": "design_abc123",
"status": "failed",
"error": {
"code": "RENDER_TIMEOUT",
"message": "Render exceeded maximum time limit"
}
}
}design.created
{
"eventId": "evt_ghi789",
"eventType": "design.created",
"timestamp": "2024-01-15T10:32:00Z",
"data": {
"designId": "design_new123",
"name": "New Design",
"width": 1920,
"height": 1080,
"createdBy": "user_abc123"
}
}Retry Policy
Failed webhook deliveries are retried with exponential backoff:
| Attempt | Delay |
|---|---|
| 1 | Immediate |
| 2 | 1 minute |
| 3 | 5 minutes |
| 4 | 30 minutes |
| 5 | 2 hours |
After 5 failed attempts, the webhook is marked as failed and no further retries occur.
Best Practices
- Respond quickly — Return 200 immediately, process asynchronously
- Handle duplicates — Use
eventIdfor idempotency - Verify signatures — Always validate the webhook signature
- Log events — Keep records for debugging
- Use HTTPS — Secure your webhook endpoint
Testing Webhooks
Test Endpoint
Send a test webhook from your dashboard (opens in a new tab):
- Go to Settings → Webhooks
- Click Test next to your webhook
- Select an event type
- Click Send Test
Local Development
Use tools like ngrok (opens in a new tab) to expose local endpoints:
ngrok http 3000Then use the ngrok URL as your webhook endpoint.
SDK Helpers
Python
from canvelete import CanveleteClient
client = CanveleteClient(api_key="YOUR_API_KEY")
# Verify webhook
is_valid = client.webhooks.verify(
payload=request_body,
signature=signature_header,
secret=webhook_secret
)TypeScript
import { CanveleteClient } from '@canveletedotcom/sdk';
const client = new CanveleteClient({ apiKey: 'YOUR_API_KEY' });
// Verify webhook
const isValid = client.webhooks.verify({
payload: requestBody,
signature: signatureHeader,
secret: webhookSecret
});Next Steps
- Rendering API — Trigger renders that send webhooks
- Integrations — Connect with Zapier and n8n
- SDKs — Use webhook helpers in your code