Understanding DoorFlow Webhooks

Real-time notifications for access events, credential changes, and door status updates

20 mins
Intermediate

Webhooks provide real-time notifications when events occur in a DoorFlow account. This guide covers webhook architecture, security, and implementation best practices.

Webhooks are Notifications, Not Data

A webhook is simply a notification that something has changed - it is not the changed data itself. When you receive a webhook, you must make a separate API call to get the current state of the resource.

We use this pattern for several important reasons:

  • Security: Sensitive data is only transferred when you make an authenticated API request, not via webhooks.
  • Data Consistency: Multiple changes might occur while your webhook is in transit. By fetching the current state, you always get the latest data.
  • Size Efficiency: Webhooks remain small and fast, only carrying the minimum information needed to identify what changed.
  • Reliability: If multiple webhooks arrive out of order, fetching the current state ensures you don't process outdated information.

Webhook Processing Flow

Initial Webhook Receipt

When your endpoint receives a webhook from DoorFlow:

  1. Validate the webhook signature
  2. Return an immediate HTTP 200 response

This confirms to DoorFlow that your endpoint successfully received the notification.

Event Processing

After receiving the webhook notification, you should:

  1. Extract the resource_url and ack_token from the webhook payload
  2. Make an API request to the resource_url, which includes the ack_token

Example:

GET https://api.doorflow.com/api/3/events/636233528?ack_token=xOu1poAPA

The ack_token serves as confirmation that you've processed this specific event by fetching its details.

Configure Your Webhook URL

A DoorFlow OAuth application can define a webhook_url through the developer portal at:

https://developer.doorflow.com

DoorFlow uses webhooks to notify your OAuth application when an event happens in your account. Webhooks are particularly useful for asynchronous events like when a person's credentials have been updated.

Supported Webhook Triggers

The following changes trigger a webhook callback:

Trigger Required Scope
PersonCredential changes account.person.readonly or account.person
Perimeter events (door forced, door extended open, door normal) account.event.perimeters_readonly
Access events account.event.access.readonly

Important: Your OAuth application must have the appropriate scopes approved on a valid refresh token to receive these webhook callbacks.

If you do not wish to receive a specific webhook for a specific DoorFlow account, ensure any current valid token does not have the scope set. For example, if you don't want Access event webhooks for Acme Inc DoorFlow account, ensure any valid token will not have those scopes.

Steps to Receive Webhooks

  1. Create a webhook endpoint as an HTTP endpoint (URL) on your server
  2. Handle requests from DoorFlow by parsing the payload and returning a 200 response status
  3. Secure your webhook by validating the HMAC signature sent by DoorFlow to ensure authenticity
  4. Use a service such as ngrok to make your webhook_url publicly accessible during development
  5. Add your temporary webhook_url through the developer portal at https://developer.doorflow.com
  6. Once a webhook request has been received from DoorFlow, use the resource_url provided to request the latest resource from our API
  7. Acknowledge the receipt of each webhook using the ack_token provided to maintain reliable delivery
  8. Test your webhook endpoint is working properly with DoorFlow
  9. Deploy your webhook endpoint to your production servers
  10. Replace your temporary webhook_url with your production webhook_url through the developer portal

Security Measures

HMAC Signature

The linchpin of our webhook security is the HMAC-SHA256 signature, included in every outgoing webhook within the Signature header. This signature is a cryptographic hash generated from the webhook payload and the shared secret key, ensuring the authenticity and integrity of the request.

Timestamp

Each webhook payload is accompanied by a timestamp within the Timestamp header. This inclusion is a security measure against replay attacks, ensuring that intercepted webhook requests cannot be reused maliciously. The timestamp is integral to the signature generation process, providing a time-sensitive layer of security that enhances the trustworthiness of the webhook interaction.

Signature Generation Process

The signature is generated by concatenating the timestamp and the serialized JSON payload, forming a string with the structure timestamp.payload. This concatenated string is then encrypted using the SHA-256 algorithm and the secret key to produce the HMAC signature. This method ensures that both the content and the timing of the message are verified, adding an extra layer of security.

Verifying Webhook Authenticity

To verify the authenticity of a received webhook:

  1. Extract the Signature: Retrieve the HMAC signature from the Signature header
  2. Recreate the Signature Data String: Concatenate the received timestamp with the received payload in the format timestamp.payload
  3. Generate Your Signature: Encrypt the combined string using the SHA-256 hashing algorithm and your secret key to produce a signature
  4. Compare Signatures: Match your generated signature against the one in the header. If they match, the webhook is confirmed to be authentic, indicating it has been sent by DoorFlow and has not been altered

This thorough validation process ensures that the webhooks you process are secure, verified, and authentic, maintaining the integrity of the information exchange.

Verifying Webhooks: Examples

Example HTTP Request

http
POST /webhook-endpoint HTTP/1.1
Host: your-webhook-endpoint.com
Content-Type: application/json
Timestamp: 1712049196
Signature: 80be869dade5c74a15326aa6e1b7a41b33540cb0c7ca4018b3feef92a7a2e270

[
  {
    "action": "CREATE",
    "resource": "https://api.doorflow.com/api/3/events/98321?ack_token=unique_acknowledgement_token",
    "resource_type": "Event",
    "resource_id": "98321",
    "account_id": "nbYfy7",
    "ack_token": "unique_acknowledgement_token"
  }
]

Verification with Python

python
import hashlib
import hmac

# Example values from the webhook request
timestamp = "1712049196"
received_signature = "da6685646a982f973f26bdfd84762e3f02a9d6676dbde0692e91267a1ebd7f6d"
payload = '[{"action":"CREATE","resource":"https://api.doorflow.com/api/3/events/98321?ack_token=unique_acknowledgement_token","resource_type":"Event","resource_id":"98321","account_id": "nbYfy7","ack_token":"unique_acknowledgement_token"}]'

# Your secret key provided by DoorFlow
secret_key = "fGdEhjYl_cdFIcAhL3Cq0kr5osdnLnMQQJEef0yWxPX"

# Generate the HMAC-SHA256 signature
signature = hmac.new(secret_key.encode(), f"{timestamp}.{payload}".encode(), hashlib.sha256).hexdigest()

# Signature validation
print("Signature is valid." if hmac.compare_digest(received_signature, signature) else "Signature is invalid.")

Verification with PHP

php
<?php
// Example values from the webhook request
$timestamp = "1712049196";
$payload = '[{"action":"CREATE","resource":"https://api.doorflow.com/api/3/events/98321?ack_token=unique_acknowledgement_token","resource_type":"Event","resource_id":"98321","account_id": "nbYfy7","ack_token":"unique_acknowledgement_token"}]';

// Your secret key provided by DoorFlow
$secret_key = "fGdEhjYl_cdFIcAhL3Cq0kr5osdnLnMQQJEef0yWxPX";

// Generate the HMAC-SHA256 signature
$string_to_sign = $timestamp . "." . $payload;
$signature = hash_hmac("sha256", $string_to_sign, $secret_key);

// Signature validation
$expected_signature = "80be869dade5c74a15326aa6e1b7a41b33540cb0c7ca4018b3feef92a7a2e270";

echo "Generated signature: " . $signature . "\n";
echo "Expected signature: " . $expected_signature . "\n";
echo "Signature matches: " . ($signature === $expected_signature ? "Yes" : "No") . "\n";

// For debugging
echo "\nString being signed:\n" . $string_to_sign . "\n";
?>

Verification with Ruby

ruby
require 'openssl'

# Example values from the webhook request
timestamp = "1712049196"
payload = '[{"action":"CREATE","resource":"https://api.doorflow.com/api/3/events/98321?ack_token=unique_acknowledgement_token","resource_type":"Event","resource_id":"98321","account_id": "nbYfy7","ack_token":"unique_acknowledgement_token"}]'
received_signature = "da6685646a982f973f26bdfd84762e3f02a9d6676dbde0692e91267a1ebd7f6d"

# Your secret key provided by DoorFlow
secret_key = "fGdEhjYl_cdFIcAhL3Cq0kr5osdnLnMQQJEef0yWxPX"

# Generate the HMAC-SHA256 signature
string_to_sign = "#{timestamp}.#{payload}"
signature = OpenSSL::HMAC.hexdigest('SHA256', secret_key, string_to_sign)

# Signature validation
if ActiveSupport::SecurityUtils.secure_compare(signature, received_signature)
  puts "Signature is valid."
else
  puts "Signature is invalid."
end

Acknowledge Token

Each webhook may include an ack_token. When provided, you must use this to acknowledge receipt of the webhook. Send the acknowledgement back to DoorFlow as a param named ack_token when accessing the associated resource.

This process ensures reliable delivery of webhooks and provides accountability to our users that the OAuth application is processing webhooks for their account.

Access Token Requirement

For a webhook to be sent, a valid access token with the appropriate scopes is required. This requirement ensures that only authorized applications can receive webhook data, protecting sensitive information from unauthorized access. The access token acts as a permission slip, indicating that the application has been granted explicit consent to receive information about specific events.

Handling Webhooks Across Multiple Accounts

In a multi-tenant system where your application receives webhooks from various accounts, associate each operation with the correct account. This ensures that actions triggered by webhooks are accurately attributed and that your application interacts with the DoorFlow API using the correct access token for each account.

Establishing an Access Token Registry

Your application should maintain a registry of access tokens, associating each token with its corresponding account_id. This registry is critical for identifying which access token to use when interacting with the DoorFlow API in response to received webhooks.

Obtaining account_id Using the OAuth 2.0 /oauth/token/info Endpoint

To associate an access token with an account_id, use the DoorFlow OAuth 2.0 /oauth/token/info endpoint. This endpoint provides metadata about an access token, including the account_id it's associated with.

Request:

bash
curl -X GET "https://api.doorflow.com/oauth/token/info" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN"

Response:

json
{
  "resource_owner_id": 44,
  "account_id": 884455,
  "scope": [
    "account.person.readonly",
    "account.event.access.readonly"
  ],
  "expires_in": 3086,
  "application": {
    "uid": "CN90U5jZxvnCaOUaNlGgMWjo5YMVo8aAg9P1mTKah-U"
  },
  "created_at": 1712498127
}

The account_id field is critical; it indicates the account the access token is associated with. This information allows your application to select the correct access token from your registry when processing webhooks.

Best Practices for Multi-Account Handling

  1. Secure Storage: Ensure the secure storage of access tokens and their associated account_ids to protect against unauthorized access
  2. Token Lifecycle Management: Implement logic to handle token expiration and renewal, ensuring your application has continuous access
  3. Minimal Scope Request: Limit the scopes requested for each token to minimize potential damage in the event of a token compromise

Processing Webhooks from Multiple Accounts

When your application receives a webhook:

  1. Identify the account context of the webhook. If not directly provided in the webhook payload, use stored data that associates webhook types or identifiers with specific accounts
  2. Retrieve the appropriate access token for the account from your registry
  3. Use this access token for any API calls made to the DoorFlow API in response to the webhook

Responding to a Webhook

Your endpoint must return a status code within the 200-299 range. Any code outside this range is considered an error and DoorFlow will retry delivery.

Example Request Body

The webhook request will be made by DoorFlow to your webhook_url defined for your application.

Example Payload:

json
[
  {
    "action": "CREATE",
    "resource": "https://api.doorflow.com/api/3/events/98321?ack_token=unique_acknowledgement_token",
    "resource_type": "Event",
    "resource_id": "98321",
    "account_id": "nbYfy7",
    "ack_token": "unique_acknowledgement_token"
  }
]

Actions

The action field in the webhook payload specifies the type of event that occurred:

Action Meaning
CREATE A new resource has been created
UPDATE An existing resource has been updated
DESTROY A resource has been destroyed

Scopes for Webhooks

To ensure your application receives the appropriate webhook events, it must request the correct scopes:

Scope Webhook Events
account.event.perimeters.readonly Perimeter events (doors forced open, left open)
account.event.access.readonly Access events (admitted, rejected)
account.person.readonly Person credential webhooks

Configure your application with the necessary scopes to ensure it receives the relevant webhook events from DoorFlow.

Redemption of Resources

Sometimes it's important that webhooks aren't sent into the wind without any confirmation that they've been consumed. This is particularly important when building security monitoring systems where you need to ensure that communications have been at least acknowledged.

To this end, we track when a webhook resource has been redeemed.

Webhook resources can be redeemed at any time. That said, we assume that this will happen within 5 seconds of being issued.

Quick Reference

Configure webhook URL: Developer Portal at https://developer.doorflow.com

Headers in webhook requests

Content-Type: application/json
Timestamp: <unix_timestamp>
Signature: <hmac_sha256_signature>

Signature format: HMAC-SHA256(secret_key, "timestamp.payload")

Required response: HTTP 200-299 status code

Payload fields

action - CREATE, UPDATE, or DESTROY
resource - URL to fetch the resource (includes ack_token)
resource_type - Type of resource (Event, PersonCredential, etc.)
resource_id - ID of the affected resource
account_id - DoorFlow account ID
ack_token - Token for acknowledging receipt

Next Steps

Need Help?

Common questions:

  • Q: How quickly should I respond to webhooks? A: Return HTTP 200 immediately, then process asynchronously.
  • Q: What if my endpoint is down? A: DoorFlow will retry delivery with exponential backoff.
  • Q: Can I test webhooks locally? A: Yes, use ngrok or similar to expose your local endpoint.
  • Q: Why am I not receiving webhooks? A: Check that you have a valid access token with the required scopes for that account.