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

  1. Log in to your CANSKAN dashboard
  2. Navigate to your organization
  3. Go to Developers > Webhooks
  4. Click Add Webhook
  5. 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
  6. Click Create Webhook
  7. 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

  1. Extract the timestamp from X-Webhook-Timestamp
  2. Concatenate the timestamp and raw request body with a period: {timestamp}.{body}
  3. Generate an HMAC-SHA256 hash using your webhook secret
  4. 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:

  1. Return quickly: Process webhooks asynchronously if needed
  2. Return 2xx: Only return success after receiving the webhook, not after processing
  3. Handle duplicates: Webhooks may be delivered more than once; use the id field for deduplication
  4. Monitor your endpoint: Check the webhook deliveries page for failures

Testing Webhooks

You can send a test webhook from the webhook details page:

  1. Go to Developers > Webhooks
  2. Click on your webhook
  3. 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:

  1. Go to your webhook settings
  2. Click Regenerate Secret
  3. Update your endpoint with the new secret
  4. The old secret will immediately stop working

Disabling Webhooks

You can temporarily disable a webhook without deleting it:

  1. Go to your webhook settings
  2. Toggle the Active switch off

Disabled webhooks will not receive any events.

Best Practices

  1. Always verify signatures: Never trust webhook payloads without verification
  2. Respond quickly: Return a 2xx response within 30 seconds
  3. Process asynchronously: Queue webhook processing for long-running tasks
  4. Handle duplicates: Use the event id to prevent duplicate processing
  5. Log everything: Keep records of received webhooks for debugging
  6. Use HTTPS: Always use HTTPS endpoints for security
  7. 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.