Home API & Developers

API & Developers

REST API, webhooks, custom integrations, and developer resources
By KYBER
3 articles

API Authentication & Getting Started

API Authentication & Getting Started The KyberAccess REST API allows you to integrate visitor management into your existing systems, automate workflows, and build custom applications. Whether you want to programmatically create pre-registrations, pull visitor data into a BI tool, or trigger actions in response to check-in events, the API provides full access to KyberAccess functionality. This guide covers how to obtain API credentials, authenticate your requests, and make your first API call. Prerequisites - A KyberAccess account with Owner or Admin role - Basic familiarity with REST APIs and HTTP requests - A tool for making API calls (e.g., cURL, Postman, or your programming language's HTTP library) Generating API Keys 1. Log in to your KyberAccess dashboard at app.kyberaccess.com. 2. Navigate to Settings from the left sidebar. 3. Click API & Integrations under the Developer section. 4. Click + Generate API Key. 5. Enter a descriptive name for the key (e.g., "CRM Integration" or "BI Dashboard"). 6. Select the key's permission scope: - Read Only — Can retrieve data but cannot create, update, or delete - Read/Write — Full access to create, read, update, and delete resources - Custom — Select specific endpoints and methods 7. Optionally set an expiration date for the key. 8. Click Generate. 9. Copy the API key immediately — it will only be displayed once. Important: Store your API key securely. Do not expose it in client-side code, public repositories, or unencrypted files. Treat it like a password. Authentication Methods KyberAccess supports two authentication methods: Method 1: JWT Bearer Token (Recommended) Use your API key to obtain a short-lived JWT token, then use that token for subsequent requests. Step 1: Obtain a JWT Token POST /api/v1/auth/token Content-Type: application/json { "api_key": "your-api-key-here" } Response: { "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", "expires_at": "2025-01-15T12:00:00Z", "token_type": "Bearer" } Step 2: Use the JWT in Requests Include the token in the Authorization header: GET /api/v1/visitors Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9... JWT tokens expire after 1 hour. When a token expires, request a new one using your API key. Method 2: API Key in Header For simpler integrations, you can pass the API key directly in a header: GET /api/v1/visitors X-API-Key: your-api-key-here Note: Direct API key authentication is simpler but less secure than JWT. We recommend JWT for production integrations. Base URL All API requests use the following base URL: https://api.kyberaccess.com/api/v1 All endpoints require HTTPS. HTTP requests are rejected. Making Your First API Call Let's retrieve a list of today's visitors: Using cURL curl -X GET "https://api.kyberaccess.com/api/v1/visitors?date=today" \ -H "Authorization: Bearer YOUR_JWT_TOKEN" \ -H "Content-Type: application/json" Using Postman 1. Open Postman and create a new GET request. 2. Enter the URL: https://api.kyberaccess.com/api/v1/visitors?date=today 3. Go to the Authorization tab, select Bearer Token, and paste your JWT. 4. Click Send. 5. The response appears in the body panel. Sample Response { "data": [ { "id": "vis_abc123", "first_name": "Jane", "last_name": "Smith", "email": "jane@example.com", "company": "Acme Corp", "host": { "id": "usr_xyz789", "name": "John Doe", "department": "Engineering" }, "check_in_time": "2025-01-15T09:30:00Z", "check_out_time": null, "status": "checked_in", "purpose": "Meeting", "id_scanned": true, "nda_signed": true, "badge_number": "V-0042", "location_id": "loc_001" } ], "meta": { "total": 1, "page": 1, "per_page": 25 } } Rate Limits To ensure fair usage, the API enforces rate limits: | Plan | Rate Limit | |------|------------| | Starter | 100 requests per minute | | Professional | 500 requests per minute | | Enterprise | 2,000 requests per minute | When you exceed the rate limit, you'll receive a 429 Too Many Requests response with headers indicating when you can retry: HTTP/1.1 429 Too Many Requests Retry-After: 30 X-RateLimit-Limit: 500 X-RateLimit-Remaining: 0 X-RateLimit-Reset: 1705312200 Pagination List endpoints return paginated results. Use query parameters to control pagination: - page — Page number (default: 1) - per_page — Results per page (default: 25, max: 100) Example: GET /api/v1/visitors?page=2&per_page=50 The response includes a meta object with pagination details: { "meta": { "total": 350, "page": 2, "per_page": 50, "total_pages": 7 } } Error Handling The API uses standard HTTP status codes: | Code | Meaning | |------|---------| | 200 | Success | | 201 | Resource created | | 400 | Bad request — check your request body or parameters | | 401 | Unauthorized — invalid or expired token | | 403 | Forbidden — insufficient permissions | | 404 | Resource not found | | 422 | Validation error — missing or invalid fields | | 429 | Rate limit exceeded | | 500 | Server error — contact support | Error responses include a descriptive message: { "error": { "code": "VALIDATION_ERROR", "message": "The 'email' field must be a valid email address.", "details": [ { "field": "email", "issue": "Invalid format" } ] } } API Key Management Viewing Active Keys 1. Go to Settings → API & Integrations. 2. The API Keys section lists all active keys with: - Key name - Last 4 characters of the key - Permission scope - Last used date - Expiration date Revoking a Key 1. Click the Revoke button next to the key. 2. Confirm the revocation. 3. The key is immediately invalidated and cannot be used. Warning: Revoking a key immediately breaks any integrations using that key. Ensure you have a replacement key configured before revoking. Rotating Keys For security best practices, rotate your API keys periodically: 1. Generate a new key. 2. Update your integrations to use the new key. 3. Verify everything works with the new key. 4. Revoke the old key. SDKs and Libraries KyberAccess provides official SDKs for popular languages: - JavaScript/Node.js — npm install kyberaccess-sdk - Python — pip install kyberaccess - Ruby — gem install kyberaccess - PHP — composer require kyberaccess/sdk SDKs handle authentication, pagination, and error handling automatically. Quick Start with Node.js const KyberAccess = require('kyberaccess-sdk'); const client = new KyberAccess({ apiKey: 'your-api-key-here' }); // List today's visitors const visitors = await client.visitors.list({ date: 'today' }); console.log(visitors.data); Sandbox Environment KyberAccess provides a sandbox environment for testing: - Sandbox URL: https://sandbox.kyberaccess.com/api/v1 - Sandbox keys are separate from production keys - Sandbox data resets every 24 hours - Use sandbox for development and testing before going to production To create a sandbox key: 1. Go to Settings → API & Integrations. 2. Click the Sandbox tab. 3. Click Generate Sandbox Key. Troubleshooting | Issue | Solution | |-------|----------| | 401 Unauthorized | Your token may be expired. Generate a new JWT. | | 403 Forbidden | Your API key may not have the required scope. Check permissions. | | Empty response | Verify filters — you may be querying a date range or location with no data. | | CORS errors in browser | The API does not support browser-side calls. Use a server-side proxy. | | Connection timeout | Check your network. Ensure you're using HTTPS. | Next Steps - API Reference: Visitors & Check-Ins — Detailed endpoint documentation for visitor operations - Webhook Configuration — Set up real-time event notifications - Explore the Postman Collection — Download from Settings → API & Integrations → Postman Collection

Last updated on Apr 25, 2026

API Reference: Visitors & Check-Ins

API Reference: Visitors & Check-Ins This reference documents all API endpoints related to visitors and check-ins in KyberAccess. Use these endpoints to list, create, update, and manage visitor records programmatically. For authentication setup and general API usage, see API Authentication & Getting Started. Visitors Endpoints List Visitors Retrieve a paginated list of visitors. GET /api/v1/visitors Query Parameters: | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | date | string | No | Filter by date: today, yesterday, or ISO 8601 date (YYYY-MM-DD) | | start_date | string | No | Start of date range (ISO 8601) | | end_date | string | No | End of date range (ISO 8601) | | status | string | No | Filter by status: checked_in, checked_out, denied, pre_registered | | location_id | string | No | Filter by location | | host_id | string | No | Filter by host | | department | string | No | Filter by department | | purpose | string | No | Filter by visit purpose | | search | string | No | Search by visitor name, email, or company | | page | integer | No | Page number (default: 1) | | per_page | integer | No | Results per page (default: 25, max: 100) | | sort | string | No | Sort field: check_in_time, name, company (default: check_in_time) | | order | string | No | Sort order: asc or desc (default: desc) | Example Request: curl -X GET "https://api.kyberaccess.com/api/v1/visitors?date=today&status=checked_in" \ -H "Authorization: Bearer YOUR_TOKEN" Response (200 OK): { "data": [ { "id": "vis_abc123", "first_name": "Jane", "last_name": "Smith", "email": "jane@example.com", "phone": "+1-555-0100", "company": "Acme Corp", "purpose": "Meeting", "host": { "id": "usr_xyz789", "name": "John Doe", "email": "john@company.com", "department": "Engineering" }, "status": "checked_in", "check_in_time": "2025-01-15T09:30:00Z", "check_out_time": null, "location": { "id": "loc_001", "name": "Main Office" }, "id_scanned": true, "id_type": "drivers_license", "nda_signed": true, "watchlist_cleared": true, "badge_number": "V-0042", "photo_url": "https://api.kyberaccess.com/photos/vis_abc123.jpg", "custom_fields": { "vehicle_plate": "ABC-1234", "laptop_serial": "SN-98765" }, "returning_visitor": true, "visit_count": 5, "created_at": "2025-01-15T09:30:00Z", "updated_at": "2025-01-15T09:30:00Z" } ], "meta": { "total": 47, "page": 1, "per_page": 25, "total_pages": 2 } } Get Single Visitor Retrieve details for a specific visitor. GET /api/v1/visitors/{visitor_id} Path Parameters: | Parameter | Type | Description | |-----------|------|-------------| | visitor_id | string | The visitor's unique ID | Example Request: curl -X GET "https://api.kyberaccess.com/api/v1/visitors/vis_abc123" \ -H "Authorization: Bearer YOUR_TOKEN" Response (200 OK): Returns the full visitor object as shown above, plus additional details: { "data": { "id": "vis_abc123", "first_name": "Jane", "last_name": "Smith", "...all standard fields...", "id_photo_url": "https://api.kyberaccess.com/photos/vis_abc123_id.jpg", "selfie_url": "https://api.kyberaccess.com/photos/vis_abc123_selfie.jpg", "photo_match_score": 0.94, "nda_document_url": "https://api.kyberaccess.com/docs/vis_abc123_nda.pdf", "screening_results": { "sex_offender": "clear", "custom_watchlist": "clear", "bolo": "clear" }, "visit_history": [ { "date": "2025-01-10", "host": "John Doe", "purpose": "Meeting" }, { "date": "2024-12-20", "host": "Jane Manager", "purpose": "Delivery" } ] } } Create a Pre-Registration Pre-register an expected visitor. POST /api/v1/visitors/pre-register Request Body: | Field | Type | Required | Description | |-------|------|----------|-------------| | first_name | string | Yes | Visitor's first name | | last_name | string | Yes | Visitor's last name | | email | string | No | Visitor's email (required for sending invite) | | phone | string | No | Visitor's phone number | | company | string | No | Visitor's company | | purpose | string | No | Purpose of visit | | host_id | string | Yes | ID of the employee being visited | | expected_date | string | Yes | Expected visit date (ISO 8601) | | expected_time | string | No | Expected arrival time (HH:MM format) | | location_id | string | No | Location ID (defaults to primary location) | | send_invite | boolean | No | Send email invite to visitor (default: true) | | custom_fields | object | No | Key-value pairs for custom fields | | notes | string | No | Internal notes about the visit | Example Request: curl -X POST "https://api.kyberaccess.com/api/v1/visitors/pre-register" \ -H "Authorization: Bearer YOUR_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "first_name": "Alice", "last_name": "Johnson", "email": "alice@partner.com", "company": "Partner Inc", "purpose": "Contract Review", "host_id": "usr_xyz789", "expected_date": "2025-01-20", "expected_time": "10:00", "send_invite": true, "custom_fields": { "vehicle_plate": "XYZ-5678" } }' Response (201 Created): { "data": { "id": "vis_def456", "status": "pre_registered", "qr_code_url": "https://api.kyberaccess.com/qr/vis_def456.png", "invite_sent": true, "...all standard fields..." } } Check In a Visitor Programmatically check in a visitor. POST /api/v1/visitors/{visitor_id}/check-in For pre-registered visitors, use their existing ID. For walk-ins, create a new visitor first: POST /api/v1/visitors/check-in Request Body (Walk-In): | Field | Type | Required | Description | |-------|------|----------|-------------| | first_name | string | Yes | Visitor's first name | | last_name | string | Yes | Visitor's last name | | email | string | No | Visitor's email | | phone | string | No | Visitor's phone | | company | string | No | Visitor's company | | purpose | string | No | Purpose of visit | | host_id | string | Yes | Host employee ID | | location_id | string | No | Location ID | | skip_screening | boolean | No | Skip watchlist screening (default: false) | | custom_fields | object | No | Custom field values | Response (201 Created): { "data": { "id": "vis_ghi789", "status": "checked_in", "check_in_time": "2025-01-15T14:00:00Z", "badge_number": "V-0043", "host_notified": true, "screening_results": { "sex_offender": "clear", "custom_watchlist": "clear", "bolo": "clear" } } } Note: If watchlist screening returns a hit, the response includes a screening_alert field and the visitor status is set to denied (if auto-deny is enabled) or flagged (if manual review is required). Check Out a Visitor POST /api/v1/visitors/{visitor_id}/check-out Request Body (optional): | Field | Type | Description | |-------|------|-------------| | notes | string | Check-out notes | | checked_out_by | string | User ID of who processed the check-out | Response (200 OK): { "data": { "id": "vis_abc123", "status": "checked_out", "check_out_time": "2025-01-15T17:30:00Z", "duration_minutes": 480 } } Bulk Check-Out Check out multiple visitors at once (useful for end-of-day processing). POST /api/v1/visitors/bulk-check-out Request Body: { "visitor_ids": ["vis_abc123", "vis_def456", "vis_ghi789"], "notes": "End of day bulk check-out" } Alternatively, check out all visitors at a location: { "location_id": "loc_001", "check_out_all": true } Response (200 OK): { "data": { "checked_out": 3, "failed": 0, "details": [ { "id": "vis_abc123", "status": "checked_out" }, { "id": "vis_def456", "status": "checked_out" }, { "id": "vis_ghi789", "status": "checked_out" } ] } } Update a Visitor Record PATCH /api/v1/visitors/{visitor_id} Request Body: Include only the fields you want to update. { "phone": "+1-555-0200", "company": "New Company Name", "custom_fields": { "vehicle_plate": "NEW-1234" } } Response (200 OK): Returns the updated visitor object. Delete a Visitor Record DELETE /api/v1/visitors/{visitor_id} This permanently deletes the visitor record. For GDPR compliance, this also removes all associated photos, documents, and screening results. Response (204 No Content) Warning: Deletion is irreversible. Consider using the archive endpoint instead if you need to retain data for compliance. Check-In Events Endpoints List Check-In Events Retrieve a log of all check-in and check-out events. GET /api/v1/check-in-events Query Parameters: | Parameter | Type | Description | |-----------|------|-------------| | start_date | string | Start of date range | | end_date | string | End of date range | | event_type | string | check_in, check_out, denied, pre_registered | | location_id | string | Filter by location | | page | integer | Page number | | per_page | integer | Results per page | Response (200 OK): { "data": [ { "id": "evt_001", "event_type": "check_in", "visitor_id": "vis_abc123", "visitor_name": "Jane Smith", "host_name": "John Doe", "location": "Main Office", "timestamp": "2025-01-15T09:30:00Z", "method": "kiosk", "badge_printed": true, "id_scanned": true, "nda_signed": true } ], "meta": { "total": 156, "page": 1, "per_page": 25 } } Host Directory Endpoints List Hosts GET /api/v1/hosts Query Parameters: | Parameter | Type | Description | |-----------|------|-------------| | search | string | Search by name or email | | department | string | Filter by department | | location_id | string | Filter by location | Response (200 OK): { "data": [ { "id": "usr_xyz789", "name": "John Doe", "email": "john@company.com", "department": "Engineering", "location": "Main Office", "visitor_count_today": 3 } ] } Active Visitors (Real-Time) Get all currently checked-in visitors. GET /api/v1/visitors/active Query Parameters: | Parameter | Type | Description | |-----------|------|-------------| | location_id | string | Filter by location | This endpoint is optimized for real-time dashboards and digital signage displays. Emergency Endpoints Get Evacuation List Retrieve all currently checked-in visitors for emergency evacuation purposes. GET /api/v1/emergency/evacuation-list Response (200 OK): { "data": { "location": "Main Office", "generated_at": "2025-01-15T14:00:00Z", "total_visitors": 12, "visitors": [ { "name": "Jane Smith", "company": "Acme Corp", "host": "John Doe", "checked_in_at": "09:30", "badge_number": "V-0042", "photo_url": "https://api.kyberaccess.com/photos/vis_abc123_thumb.jpg" } ] } } Trigger Lockdown POST /api/v1/emergency/lockdown Request Body: { "location_id": "loc_001", "reason": "Security threat", "notify_all": true } Warning: This endpoint triggers immediate lockdown and sends emergency notifications to all staff. Use with caution. Troubleshooting | Issue | Solution | |-------|----------| | 404 on visitor endpoint | Verify the visitor ID is correct. The visitor may have been deleted. | | Screening results not included | Screening may be disabled for your account. Enable it under Settings → Security. | | Pre-registration invite not sent | Verify the visitor's email is provided and send_invite is true. | | Check-in returns 409 Conflict | The visitor is already checked in. Check them out first. | | Photo URLs returning 403 | Photo URLs expire after 24 hours. Fetch the visitor record again for fresh URLs. |

Last updated on Apr 25, 2026

Webhook Configuration

Webhook Configuration Webhooks allow KyberAccess to send real-time notifications to your external systems whenever specific events occur — such as a visitor checking in, a watchlist hit being detected, or an emergency lockdown being triggered. Instead of repeatedly polling the API for changes, webhooks push data to your server the moment something happens. This guide covers how to set up, test, secure, and troubleshoot webhooks. How Webhooks Work 1. You configure a webhook endpoint (a URL on your server). 2. You select which events should trigger the webhook. 3. When a matching event occurs in KyberAccess, an HTTP POST request is sent to your endpoint with the event data. 4. Your server processes the data and returns a 200 response to acknowledge receipt. Setting Up a Webhook Step 1: Navigate to Webhook Settings 1. Log in to your KyberAccess dashboard at app.kyberaccess.com. 2. Navigate to Settings from the left sidebar. 3. Click API & Integrations under the Developer section. 4. Select the Webhooks tab. Step 2: Create a New Webhook 1. Click + Add Webhook. 2. Configure the following fields: - Name — A descriptive name (e.g., "CRM Visitor Sync" or "Slack Notifications") - Endpoint URL — The HTTPS URL where events will be sent (e.g., https://yourserver.com/webhooks/kyberaccess) - Description — Optional notes about the webhook's purpose 3. Click Next to proceed to event selection. Important: The endpoint URL must use HTTPS. HTTP endpoints are not accepted for security reasons. Step 3: Select Events Choose which events should trigger this webhook: Visitor Events: - visitor.checked_in — A visitor has checked in - visitor.checked_out — A visitor has checked out - visitor.denied — A visitor was denied entry - visitor.pre_registered — A new pre-registration was created - visitor.pre_registration_cancelled — A pre-registration was cancelled - visitor.updated — A visitor record was modified - visitor.deleted — A visitor record was deleted Security Events: - screening.watchlist_hit — A visitor matched a watchlist entry - screening.bolo_alert — A BOLO alert was triggered - screening.cleared — A visitor passed all screening checks Emergency Events: - emergency.lockdown_activated — A lockdown was triggered - emergency.lockdown_deactivated — A lockdown was lifted - emergency.evacuation_activated — Evacuation mode was activated Document Events: - document.nda_signed — A visitor signed an NDA/waiver - document.nda_declined — A visitor declined to sign Delivery Events: - delivery.received — A package was logged - delivery.picked_up — A package was picked up - delivery.notified — A recipient was notified about a delivery System Events: - user.login — A team member logged in - user.login_failed — A failed login attempt - settings.updated — System settings were changed Check the events you want and click Next. Step 4: Configure Security 1. Signing Secret — KyberAccess generates a unique signing secret for this webhook. Copy it and store it securely. 2. IP Allowlist (optional) — Restrict webhook delivery to specific source IPs from KyberAccess. 3. Click Create Webhook. Webhook Payload Format When an event occurs, KyberAccess sends an HTTP POST request to your endpoint with the following structure: Headers Content-Type: application/json X-KyberAccess-Event: visitor.checked_in X-KyberAccess-Delivery: dlv_abc123 X-KyberAccess-Timestamp: 1705312200 X-KyberAccess-Signature: sha256=a1b2c3d4e5f6... Body { "id": "evt_001", "event": "visitor.checked_in", "timestamp": "2025-01-15T09:30:00Z", "data": { "visitor": { "id": "vis_abc123", "first_name": "Jane", "last_name": "Smith", "email": "jane@example.com", "company": "Acme Corp", "purpose": "Meeting" }, "host": { "id": "usr_xyz789", "name": "John Doe", "department": "Engineering" }, "location": { "id": "loc_001", "name": "Main Office" }, "check_in_time": "2025-01-15T09:30:00Z", "badge_number": "V-0042", "id_scanned": true, "nda_signed": true, "screening_status": "cleared" }, "account_id": "acc_001" } Verifying Webhook Signatures To ensure webhook requests genuinely come from KyberAccess, verify the signature included in every request. Verification Process 1. Extract the X-KyberAccess-Signature header value. 2. Extract the X-KyberAccess-Timestamp header value. 3. Concatenate the timestamp and the raw request body with a period: timestamp.body. 4. Compute an HMAC-SHA256 hash using your webhook's signing secret. 5. Compare your computed hash with the signature from the header. Node.js Example const crypto = require('crypto'); function verifyWebhook(req, signingSecret) { const signature = req.headers['x-kyberaccess-signature']; const timestamp = req.headers['x-kyberaccess-timestamp']; const body = JSON.stringify(req.body); const payload = `${timestamp}.${body}`; const expectedSignature = 'sha256=' + crypto.createHmac('sha256', signingSecret) .update(payload) .digest('hex'); return crypto.timingSafeEqual( Buffer.from(signature), Buffer.from(expectedSignature) ); } Python Example import hmac import hashlib def verify_webhook(payload, timestamp, signature, signing_secret): message = f"{timestamp}.{payload}" expected = 'sha256=' + hmac.new( signing_secret.encode(), message.encode(), hashlib.sha256 ).hexdigest() return hmac.compare_digest(signature, expected) Important: Always use constant-time comparison functions (like timingSafeEqual or compare_digest) to prevent timing attacks. Retry Policy If your endpoint fails to respond with a 2xx status code, KyberAccess retries the delivery: | Attempt | Delay | |---------|-------| | 1st retry | 1 minute | | 2nd retry | 5 minutes | | 3rd retry | 30 minutes | | 4th retry | 2 hours | | 5th retry | 12 hours | After 5 failed retries, the delivery is marked as failed and no further attempts are made. Note: Your endpoint must respond within 30 seconds. If processing takes longer, return a 200 immediately and process the event asynchronously. Testing Webhooks Built-In Test Tool 1. Go to Settings → API & Integrations → Webhooks. 2. Click on the webhook you want to test. 3. Click the Test button. 4. Select a sample event type from the dropdown. 5. Click Send Test Event. 6. KyberAccess sends a test payload to your endpoint and displays the response. Using a Testing Service For development, you can use services like: - webhook.site — Provides a temporary URL that captures and displays incoming webhooks - ngrok — Exposes your local development server to the internet - RequestBin — Captures and inspects HTTP requests Managing Webhooks Viewing Delivery Logs 1. Click on a webhook in the Webhooks tab. 2. Select the Deliveries tab. 3. Each delivery shows: - Event Type — What triggered the webhook - Timestamp — When the event occurred - Response Code — Your server's HTTP response code - Response Time — How long your server took to respond - Status — Delivered, Retrying, or Failed 4. Click on any delivery to see the full request payload and response. Pausing a Webhook 1. Click the toggle next to the webhook to pause it. 2. Events are not queued while paused — they are skipped entirely. 3. Toggle it back on to resume deliveries. Editing a Webhook 1. Click the pencil icon next to the webhook. 2. Modify the endpoint URL, events, or security settings. 3. Click Save Changes. Note: Changing the endpoint URL does not change the signing secret. You can regenerate the secret manually if needed. Deleting a Webhook 1. Click the trash icon next to the webhook. 2. Confirm the deletion. 3. All delivery history for this webhook is also deleted. Multiple Webhooks You can create multiple webhooks for different purposes: - CRM Integration — Subscribe to visitor.checked_in and visitor.checked_out to sync visitor data with your CRM - Security Monitoring — Subscribe to screening.watchlist_hit and emergency.* events for your security operations center - Slack Bot — Subscribe to visitor events to post check-in notifications in Slack - Analytics Pipeline — Subscribe to all events to feed data into your data warehouse Each webhook has its own endpoint, events, and signing secret. Webhook Limits | Plan | Max Webhooks | Events per Second | |------|-------------|-------------------| | Starter | 3 | 10 | | Professional | 10 | 50 | | Enterprise | Unlimited | 200 | Troubleshooting | Issue | Solution | |-------|----------| | Webhooks not arriving | Check that the webhook is active (not paused). Verify the endpoint URL is correct and accessible. | | Signature verification failing | Ensure you're using the correct signing secret. Verify you're concatenating timestamp and body correctly. | | 408 Timeout errors | Your endpoint is taking too long. Return a 200 immediately and process asynchronously. | | Receiving duplicate events | Implement idempotency using the X-KyberAccess-Delivery header as a deduplication key. | | SSL/TLS errors | Ensure your endpoint has a valid SSL certificate. Self-signed certificates are not accepted. | | Webhook disabled automatically | After 100 consecutive failures, the webhook is automatically disabled. Fix the issue and re-enable it. | Best Practices - Respond quickly — Return a 200 status immediately, then process the event in a background job. - Verify signatures — Always validate the webhook signature to prevent spoofed requests. - Implement idempotency — Use the delivery ID to handle duplicate deliveries gracefully. - Monitor delivery logs — Check the delivery logs weekly for failures or high response times. - Use separate webhooks — Don't subscribe one webhook to every event. Create targeted webhooks for specific integrations. - Set up alerts — Monitor your webhook endpoint's availability and set up alerts for downtime.

Last updated on Apr 25, 2026