Webhooks
Webhooks
Webhooks allow your applications to receive real-time notifications when events happen in CraftDesk. Instead of continuously polling the API for changes, subscribe to events and react immediately when they occur.
How Webhooks Work
When you subscribe to a webhook:
- You specify an event type (e.g., "project.created")
- CraftDesk sends an HTTP POST request to your endpoint with event details
- Your application receives the notification and responds with HTTP 200
- CraftDesk retries failed deliveries for up to 24 hours
This is much more efficient than polling and enables real-time integrations.
Subscribing to Events
Set up webhooks in Settings → Integrations → Webhooks:
- Click Add Webhook
- Enter the URL where you want to receive notifications
- Select which events to subscribe to
- Optionally, set a custom secret for payload verification
- Click Create
Your endpoint should be a publicly accessible HTTPS URL that accepts POST requests.
Supported Events
CraftDesk currently supports these webhook events:
Project Events
project.created— A new project was createdproject.updated— Project settings were modifiedproject.deleted— A project was deletedproject.archived— A project was archivedproject.unarchived— An archived project was restored
Member Events
member.invited— A team member was invitedmember.joined— An invited member accepted and joinedmember.role_changed— A member's role was updatedmember.removed— A member was removed from the workspace
Task Events
task.created— A new task was createdtask.updated— A task was modifiedtask.completed— A task was marked completetask.deleted— A task was deleted
Subscription Events
subscription.created— A new subscription startedsubscription.upgraded— A subscription was upgradedsubscription.downgraded— A subscription was downgradedsubscription.cancelled— A subscription was cancelled
Webhook Payload Structure
Webhook payloads are JSON POST requests with this structure:
{
"id": "evt_550e8400e29b41d4a716446655440000",
"event": "project.created",
"timestamp": "2025-03-23T14:35:21Z",
"workspace_id": "ws_550e8400e29b41d4a716446655440000",
"data": {
"id": "proj_550e8400e29b41d4a716446655440000",
"name": "Website Redesign",
"slug": "website-redesign",
"description": "Modernize our web presence",
"created_at": "2025-03-23T14:35:21Z"
}
}
Key Fields
id— Unique event identifierevent— The event type that triggered this notificationtimestamp— When the event occurred (ISO 8601 format)workspace_id— The workspace where the event occurreddata— Event-specific details
Verifying Webhook Signatures
Verify that webhooks come from CraftDesk by checking the signature. Every webhook includes an X-Webhook-Signature header containing an HMAC-SHA256 hash of the payload:
import hmac
import hashlib
import json
def verify_webhook(payload_body, signature, secret):
"""Verify that a webhook came from CraftDesk."""
expected_signature = hmac.new(
secret.encode(),
payload_body.encode(),
hashlib.sha256
).hexdigest()
return hmac.compare_digest(signature, expected_signature)
# In your Flask handler
@app.route("/webhooks/craftdesk", methods=["POST"])
def handle_webhook():
payload = request.get_data()
signature = request.headers.get("X-Webhook-Signature")
if not verify_webhook(payload, signature, os.getenv("WEBHOOK_SECRET")):
return "Unauthorized", 401
event = json.loads(payload)
if event["event"] == "project.created":
# Handle project creation
project = event["data"]
print(f"New project: {project['name']}")
return "OK", 200
Handling Webhook Events
Your endpoint should respond with HTTP 200 within 30 seconds to acknowledge receipt. Process events asynchronously to avoid timeouts:
@app.route("/webhooks/craftdesk", methods=["POST"])
def handle_webhook():
event = request.json
# Queue the event for async processing
background_job.queue("process_webhook", event)
# Return immediately
return "OK", 200
def process_webhook(event):
"""Process webhook asynchronously."""
if event["event"] == "member.joined":
new_member = event["data"]
send_welcome_email(new_member["email"])
# ... handle other events
Retry Policy
CraftDesk retries failed deliveries with exponential backoff:
- 1st attempt: Immediately
- 2nd attempt: 5 minutes later
- 3rd attempt: 30 minutes later
- 4th attempt: 2 hours later
- 5th attempt: 24 hours later
If all retries fail after 24 hours, the webhook event is marked failed. Check your webhook status in the dashboard to see delivery history.
Testing Webhooks Locally
Use a tool like ngrok to expose your local development server:
# In one terminal, start your app
python app.py
# In another terminal, expose it to the internet
ngrok http 5000
Then register the ngrok URL (e.g., https://xyz.ngrok.io/webhooks/craftdesk) in the CraftDesk dashboard. Use CraftDesk's webhook testing tools to send test events to your endpoint.
Webhook Best Practices
Always Verify Signatures — Never trust webhooks without signature verification.
Idempotency — Handle duplicate webhook deliveries gracefully. Use the event id to deduplicate.
Fast Responses — Return HTTP 200 quickly. Process events asynchronously.
Error Handling — Log failures and monitor webhook health. Return non-200 to trigger retries for genuine failures.
Rate Limiting — Be prepared for many simultaneous webhook deliveries during large operations.
Monitoring Webhooks
In Settings → Integrations → Webhooks, you can:
- View delivery history for each webhook
- See response status codes and error messages
- Manually retry failed deliveries
- Temporarily disable webhooks for debugging
What's Next
- API Overview — Understand the CraftDesk API
- Authentication — Manage API keys
- API Reference — Complete webhook payload documentation (craftdesk.app/api/docs)