Webhooks
Receive real-time HTTP notifications when events occur in your organization. When an event triggers, CANSKAN sends an HTTP POST request to your configured URL with details about the event.
Setting Up Webhooks
Creating a Webhook
- Log in to your CANSKAN dashboard
- Navigate to your organization
- Go to Developers > Webhooks
- Click Add Webhook
- Configure your endpoint:
- Name: A descriptive name (e.g., "Production CRM Sync")
- Payload URL: Your HTTPS endpoint that will receive webhook events
- Events: Select which events should trigger this webhook
- Click Create Webhook
- Important: Copy your signing secret immediately - it will only be shown once
Webhook Endpoint Requirements
Your endpoint must:
- Be publicly accessible via HTTPS
- Accept POST requests
- Return a 2xx status code within 30 seconds
- Handle JSON payloads
Available Events
CANSKAN sends webhooks for the following events:
Attendee Events
| Event | Description |
|---|---|
attendee.registered |
When someone registers for an event |
attendee.checked_in |
When an attendee checks in |
attendee.cancelled |
When an attendee cancels their registration |
Event Events
| Event | Description |
|---|---|
event.created |
When a new event is created |
event.updated |
When an event is updated |
event.deleted |
When an event is deleted |
Team Events
| Event | Description |
|---|---|
team.member_added |
When a member is added to a team |
team.member_removed |
When a member is removed from a team |
Organization Events
| Event | Description |
|---|---|
organization.member_added |
When a member joins the organization |
organization.member_removed |
When a member leaves the organization |
Webhook Payload
All webhook payloads follow this structure:
{
"id": "evt_abc123xyz789",
"type": "attendee.checked_in",
"created_at": "2025-01-15T10:30:00.000000Z",
"organization": {
"id": 1,
"slug": "my-organization",
"name": "My Organization"
},
"data": {
// Event-specific data
}
}Payload Fields
| Field | Type | Description |
|---|---|---|
id |
string | Unique identifier for this event (e.g., evt_abc123) |
type |
string | The event type that triggered this webhook |
created_at |
string | ISO 8601 timestamp when the event occurred |
organization |
object | Information about the organization |
data |
object | Event-specific data (see examples below) |
Example Payloads
attendee.registered
{
"id": "evt_reg_abc123",
"type": "attendee.registered",
"created_at": "2025-01-15T09:00:00.000000Z",
"organization": {
"id": 1,
"slug": "tech-conference",
"name": "Tech Conference 2025"
},
"data": {
"attendee": {
"id": 123,
"email": "john@example.com",
"name": "John Doe",
"status": "confirmed",
"is_guest": false,
"data": {
"company": "Acme Inc",
"job_title": "Developer"
},
"created_at": "2025-01-15T09:00:00.000000Z"
},
"event": {
"id": 456,
"uuid": "f90743ac-dbf5-4776-8f19-ebe1321268bf",
"name": "Tech Conference 2025"
}
}
}attendee.checked_in
{
"id": "evt_checkin_abc123",
"type": "attendee.checked_in",
"created_at": "2025-01-15T10:30:00.000000Z",
"organization": {
"id": 1,
"slug": "tech-conference",
"name": "Tech Conference 2025"
},
"data": {
"attendee": {
"id": 123,
"email": "john@example.com",
"name": "John Doe",
"status": "confirmed",
"is_guest": false,
"data": {
"company": "Acme Inc",
"job_title": "Developer"
},
"checked_in_at": "2025-01-15T10:30:00.000000Z",
"checked_in_by": "Jane Smith",
"created_at": "2025-01-15T09:00:00.000000Z"
},
"event": {
"id": 456,
"uuid": "f90743ac-dbf5-4776-8f19-ebe1321268bf",
"name": "Tech Conference 2025"
}
}
}attendee.cancelled
{
"id": "evt_cancel_abc123",
"type": "attendee.cancelled",
"created_at": "2025-01-15T11:00:00.000000Z",
"organization": {
"id": 1,
"slug": "tech-conference",
"name": "Tech Conference 2025"
},
"data": {
"attendee": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"email": "john@example.com",
"name": "John Doe",
"status": "canceled",
"registered_at": "2025-01-15T09:00:00.000000Z"
},
"event": {
"id": "f90743ac-dbf5-4776-8f19-ebe1321268bf",
"name": "Tech Conference 2025",
"starts_at": "2025-01-20T09:00:00.000000Z"
}
}
}event.created
{
"id": "evt_created_xyz789",
"type": "event.created",
"created_at": "2025-01-10T14:00:00.000000Z",
"organization": {
"id": 1,
"slug": "tech-conference",
"name": "Tech Conference 2025"
},
"data": {
"event": {
"id": 789,
"uuid": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"name": "Workshop: Building APIs",
"description": "Learn how to build robust APIs",
"status": "draft",
"location": "Room 101",
"seats": 50,
"start_date": "2025-02-01T09:00:00.000000Z",
"end_date": "2025-02-01T12:00:00.000000Z",
"registration_start": "2025-01-15T00:00:00.000000Z",
"registration_end": "2025-01-31T23:59:59.000000Z",
"created_by": "Jane Smith",
"created_at": "2025-01-10T14:00:00.000000Z"
}
}
}event.updated
{
"id": "evt_updated_xyz789",
"type": "event.updated",
"created_at": "2025-01-12T16:00:00.000000Z",
"organization": {
"id": 1,
"slug": "tech-conference",
"name": "Tech Conference 2025"
},
"data": {
"event": {
"id": 789,
"uuid": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"name": "Workshop: Building REST APIs",
"description": "Learn how to build robust REST APIs",
"status": "published",
"location": "Room 101",
"seats": 75,
"start_date": "2025-02-01T09:00:00.000000Z",
"end_date": "2025-02-01T12:00:00.000000Z",
"registration_start": "2025-01-15T00:00:00.000000Z",
"registration_end": "2025-01-31T23:59:59.000000Z",
"updated_at": "2025-01-12T16:00:00.000000Z"
},
"changed_attributes": ["name", "description", "status", "seats"]
}
}event.deleted
{
"id": "evt_deleted_xyz789",
"type": "event.deleted",
"created_at": "2025-01-20T10:00:00.000000Z",
"organization": {
"id": 1,
"slug": "tech-conference",
"name": "Tech Conference 2025"
},
"data": {
"event": {
"id": 789,
"uuid": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"name": "Workshop: Building REST APIs",
"status": "draft",
"deleted_at": "2025-01-20T10:00:00.000000Z"
}
}
}team.member_added
{
"id": "evt_team_add_abc123",
"type": "team.member_added",
"created_at": "2025-01-15T11:00:00.000000Z",
"organization": {
"id": 1,
"slug": "tech-conference",
"name": "Tech Conference 2025"
},
"data": {
"team": {
"id": 10,
"uuid": "team-uuid-here",
"name": "Event Staff"
},
"member": {
"id": 42,
"email": "newmember@example.com",
"name": "New Member",
"role": "member"
},
"added_at": "2025-01-15T11:00:00.000000Z"
}
}team.member_removed
{
"id": "evt_team_rm_abc123",
"type": "team.member_removed",
"created_at": "2025-01-16T09:00:00.000000Z",
"organization": {
"id": 1,
"slug": "tech-conference",
"name": "Tech Conference 2025"
},
"data": {
"team": {
"id": 10,
"uuid": "team-uuid-here",
"name": "Event Staff"
},
"member": {
"id": 42,
"email": "formermember@example.com",
"name": "Former Member"
},
"removed_at": "2025-01-16T09:00:00.000000Z"
}
}organization.member_added
{
"id": "evt_org_add_abc123",
"type": "organization.member_added",
"created_at": "2025-01-15T11:00:00.000000Z",
"organization": {
"id": 1,
"slug": "tech-conference",
"name": "Tech Conference 2025"
},
"data": {
"organization": {
"id": 1,
"slug": "tech-conference",
"name": "Tech Conference 2025"
},
"member": {
"id": 42,
"email": "newmember@example.com",
"name": "New Member",
"role": "member"
},
"added_at": "2025-01-15T11:00:00.000000Z"
}
}organization.member_removed
{
"id": "evt_org_rm_abc123",
"type": "organization.member_removed",
"created_at": "2025-01-16T09:00:00.000000Z",
"organization": {
"id": 1,
"slug": "tech-conference",
"name": "Tech Conference 2025"
},
"data": {
"organization": {
"id": 1,
"slug": "tech-conference",
"name": "Tech Conference 2025"
},
"member": {
"id": 42,
"email": "formermember@example.com",
"name": "Former Member"
},
"removed_at": "2025-01-16T09:00:00.000000Z"
}
}HTTP Headers
Each webhook request includes the following headers:
| Header | Description |
|---|---|
Content-Type |
Always application/json |
User-Agent |
CANSKAN-Webhook/1.0 |
X-Webhook-ID |
The UUID of the webhook configuration |
X-Webhook-Signature |
HMAC-SHA256 signature for verification |
X-Webhook-Timestamp |
Unix timestamp when the request was sent |
Verifying Webhook Signatures
To ensure webhooks are genuinely from CANSKAN, verify the signature using your webhook secret.
Signature Format
The X-Webhook-Signature header contains a signature in the format:
sha256=<signature>Verification Process
- Extract the timestamp from
X-Webhook-Timestamp - Concatenate the timestamp and raw request body with a period:
{timestamp}.{body} - Generate an HMAC-SHA256 hash using your webhook secret
- Compare your computed signature with the one in the header
Code Examples
Timestamp Validation
To prevent replay attacks, also validate that the timestamp is recent (e.g., within 5 minutes):
const MAX_AGE_SECONDS = 300; // 5 minutes
function isTimestampValid(timestamp) {
const now = Math.floor(Date.now() / 1000);
const age = now - parseInt(timestamp);
return age >= 0 && age <= MAX_AGE_SECONDS;
}Retry Logic
If your endpoint fails to respond with a 2xx status code, CANSKAN will retry the webhook with exponential backoff:
| Attempt | Delay |
|---|---|
| 1st retry | 1 minute |
| 2nd retry | 5 minutes |
| 3rd retry | 15 minutes |
| 4th retry | 1 hour |
| 5th retry | 2 hours |
After 5 failed attempts, the webhook delivery is marked as permanently failed.
Handling Failures
To minimize failed deliveries:
- Return quickly: Process webhooks asynchronously if needed
- Return 2xx: Only return success after receiving the webhook, not after processing
- Handle duplicates: Webhooks may be delivered more than once; use the
idfield for deduplication - Monitor your endpoint: Check the webhook deliveries page for failures
Testing Webhooks
You can send a test webhook from the webhook details page:
- Go to Developers > Webhooks
- Click on your webhook
- Click Send Test Webhook
The test webhook has the event type test.webhook with a sample payload.
Managing Webhooks
Viewing Deliveries
Each webhook has a delivery log showing:
- Event type
- Delivery status (success/failed)
- Response status code
- Response time
- Timestamp
Regenerating Secrets
If you believe your webhook secret has been compromised:
- Go to your webhook settings
- Click Regenerate Secret
- Update your endpoint with the new secret
- The old secret will immediately stop working
Disabling Webhooks
You can temporarily disable a webhook without deleting it:
- Go to your webhook settings
- Toggle the Active switch off
Disabled webhooks will not receive any events.
Best Practices
- Always verify signatures: Never trust webhook payloads without verification
- Respond quickly: Return a 2xx response within 30 seconds
- Process asynchronously: Queue webhook processing for long-running tasks
- Handle duplicates: Use the event
idto prevent duplicate processing - Log everything: Keep records of received webhooks for debugging
- Use HTTPS: Always use HTTPS endpoints for security
- Validate timestamps: Reject webhooks with old timestamps to prevent replay attacks
Troubleshooting
Common Issues
Webhook not receiving events
- Verify your endpoint is publicly accessible
- Check that the webhook is active
- Ensure you've subscribed to the correct events
Signature verification failing
- Verify you're using the correct secret
- Ensure you're using the raw request body (not parsed JSON)
- Check that you're including the timestamp in the signed payload
Timeouts
- Your endpoint must respond within 30 seconds
- Consider processing webhooks asynchronously
Need Help?
If you're having trouble with webhooks, contact us at support@canskan.com.