Note: Webhooks are free during the initial rollout. In the future, usage-based pricing will apply after the free tier is exhausted. We’ll notify you before packages are introduced.
Complete Guide to Configuring and Using Webhooks in eRS
1. Introduction
Webhooks in eRS allow you to receive real-time notifications when specific events occur in your system. This enables seamless integration with external systems, automated workflows, and event-driven architectures.
Key Concepts
- Webhook: A configuration that defines where to send event notifications (URL, security settings).
- Trigger: Defines which entity and event combination will activate the webhook.
- Filter: Optional conditions that determine when a trigger should fire.
- Payload: The JSON data sent to your webhook URL when an event occurs.
System Limits
- Maximum Webhooks per Account: 5
- Maximum Retries: 3
- Signing Key Length: 16-64 characters
- URL Length: Maximum 255 characters
2. Webhook Registration
1. Create a Webhook
To register a webhook, you need to provide the following information:
Required Fields
Field | Type | Description |
|---|---|---|
Name | String | A descriptive name for your webhook (e.g., "Production Resource Update"). Max 255 characters. |
Target URL | HTTP/HTTPS URL | Should be HTTP or HTTPS, unique across all webhooks. (e.g., |
Optional Fields
Field | Type | Description |
|---|---|---|
Description | String | Optional description of the webhook's purpose. Max 255 characters. |
Status | Boolean | Controls whether the webhook is active. Default: |
Encryption Payload | Boolean | Whether to encrypt the payload before sending. Default: |
Encryption Key | String | Encryption key for payload encryption (AES-256-CBC). Length: 16-64 characters. Required if Encryption Payload is true. |
2. URL Verification
eRS performs URL verification to ensure your endpoint is reachable and correctly configured.
Verification Process
Automatic Verification (when validation token is not provided):
1. eRS sends a POST request to your URL with a challenge payload:
{
"challenge": "accafc6d-e03b-4a39-a13c-d96cc8c9f897"
}2. Your endpoint must respond with HTTP 200 and echo the challenge:
{
"challenge": "accafc6d-e03b-4a39-a13c-d96cc8c9f897"
}3. If verification fails, webhook will not be registered.
Verification Failure: If URL verification fails, you'd see:
Example: Registering a Webhook
Constraints
- URL Uniqueness: Each URL can only be used by one webhook
- Protocol: HTTP or HTTPS URLs are accepted
- Encryption Consistency: If Encryption Payload is true, Encryption Key is mandatory
- URL Format: Must be a valid HTTP/HTTPS URL with domain and TLD
3. Trigger Creation
A trigger defines when a webhook should fire by specifying which entity type to monitor, which event(s) to listen for, and optional filter conditions.
Step 1: Select Entity
Entity | ID | Available Events |
|---|---|---|
Resource | 1 | Create, Update, Delete, Rate Create, Rate Update, Rate Delete |
Project | 2 | Create, Update, Delete, Task Create, Task Update, Task Delete, Rate Create, Rate Update, Rate Delete |
Booking | 4 | Create, Update, Delete |
Role Rate | 8 | Create, Update, Delete Financial Module Required |
Timesheet | 16 | Create, Update, Delete Timesheet Module Required |
Requirement | 32 | Create, Update, Delete SchedulingPlus Module Required |
Note: The following rate events require the Financial module:
• Resource – Create Rate, Update Rate, Delete Rate
• Project – Create Rate, Update Rate, Delete Rate
Step 2: Select Event(s)
Standard Events
- Create (ID: 1): New entity created
- Update (ID: 2): Existing entity modified
- Delete (ID: 3): Entity deleted
Project-Specific Events
- Task Create (ID: 4): New task added to project
- Task Update (ID: 5): Task modified
- Task Delete (ID: 6): Task removed
Rate Events Financial Module
- Rate Create (ID: 7): New rate created
- Rate Update (ID: 8): Rate modified
- Rate Delete (ID: 9): Rate deleted
Example: Creating a Single Event Trigger
Example: Creating Multiple Triggers (Bulk Creation)
You can create multiple triggers at once without filters. This is useful for quickly setting up webhooks for multiple entities and events.
Note: Filters are not supported in bulk trigger creation - all triggers created this way will fire for all events of the specified entities.
Important:
- Filters cannot be applied when creating multiple triggers.
- To add filters, create triggers individually or individually add filters to each existing trigger.
- All triggers in the bulk request share the same status, default = true.
4. Filter Configuration
5. Webhook Delivery & Retry Mechanism
Delivery Process
- Event Occurs: An entity event (create/update/delete) happens in eRS.
- Trigger Matching: System checks if any active triggers match the entity/event.
- Filter Evaluation: If filters exist, conditions are evaluated.
- Payload Preparation: Entity data is serialised into JSON.
- Encryption (if enabled): Payload is encrypted using AES-256-CBC.
- HTTP POST: Payload is sent to your webhook URL.
- Response Handling: System processes the HTTP response.
Payload Structure
The webhook payload contains comprehensive information about the event, including the complete entity document, metadata, and change tracking.
Payload Fields
user_name: Name of the user who triggered the event.user_id: ID of the user who triggered the event.account_id: Account ID where the event occurred.timestamp: ISO 8601 timestamp of when the event occurred.entity: Entity type (for example, "resource", "project", "booking").request_id: Unique identifier for this event request.event: Event type (for example, "create", "update", "delete").idempotency_key: Idempotency key from the API request headers (null if not provided by the API caller).details: Complete current state of the entity with all fields.old: Previous state of the changed fields. This is included only in update events; it is not included in create or delete events because there is no prior state to compare against.
Example Payload
Here's a complete example payload for a Resource Update event:
{
"user_name": "albert murphy",
"user_id": 38,
"account_id": 25001051,
"timestamp": {
"$date": "2025-12-01T07:05:50.966Z"
},
"entity": "resource",
"request_id": "5a7cd018-59e6-4139-8c2c-8228acfed616",
"event": "update",
"idempotency_key": null,
"details": {
"id": 156,
"resource_type": {
"name": "Personnel",
"id": 1
},
"first_name": "Albert",
"last_name": "Murphy",
"start_date": "2025-07-29",
"last_date": null,
"email": "albertmurphy@enbraun.com",
"phone": null,
"udf_contract_type": {
"name": "Employee",
"description": null,
"id": 334
},
"calendar": {
"name": "Default",
"id": 1
},
"udf_mapped_user": {
"id": 38,
"name": "albert murphy"
},
"overload_threshold": "100",
"udf_field_1": null,
"udf_field_2": "asdas",
"roles": [
{
"name": "Business Analyst",
"description": null,
"id": 5
},
{
"name": "Data Analyst",
"description": null,
"id": 6
},
{
"name": "Mechanical Engineer",
"description": null,
"id": 1
}
],
"udf_skills": [
{
"name": "Excel",
"description": null,
"id": 337
},
{
"name": "Customer Relationship Management",
"description": null,
"id": 339
},
{
"name": "Data Analyst",
"description": null,
"id": 341
},
{
"name": "Software Developer",
"description": null,
"id": 342
}
],
"udf_qualifications": [
{
"name": "Bachelor of Electronics Engineering",
"description": null,
"id": 386
},
{
"name": "Masters of Business Administration",
"description": null,
"id": 391
}
],
"udf_supervisor": {
"id": 38,
"name": "albert murphy"
},
"udf_team": null,
"udf_department": {
"name": "Information & Technology",
"description": null,
"id": 381
},
"udf_location": {
"name": "London",
"description": null,
"id": 393
},
"tags": [
"Senior Business Analyst"
],
"disable_parallel_booking": "False",
"udf_mlrt": "$$<p>This is a Multi line Rich Text</p>$$",
"created_on": "2025-11-25T09:15:53.325+0000",
"created_by": {
"id": 38,
"name": "albert murphy"
},
"modified_by": {
"id": 38,
"name": "albert murphy"
},
"modified_on": "2025-12-01T07:04:23.811+0000",
"version": "9"
},
"old": {
"roles": [
{
"name": "Business Analyst",
"description": null,
"id": 5
},
{
"name": "Data Analyst",
"description": null,
"id": 6
}
],
"modified_on": "2025-12-01T07:01:38.532+0000",
"version": "8"
}
}Notes on Payload:
- The details object contains the complete current state of the entity.
- The old object contains only the fields that changed. Important: This object is included only in update events. It is not present in create events (no previous state exists) or delete events (the entity is being removed).
- The idempotency_key field is populated from the idempotency_key HTTP header included in API calls to eRS. If the API caller does not include this header, the value will be null in the webhook payload. This enables API consumers to track and deduplicate webhook deliveries.
- UDF (User Defined Fields) are prefixed with udf_ and include their full metadata.
- Related entities (like roles, skills) are included as nested objects with id, name, and description.
- Timestamps are in ISO 8601 format.
- The version field tracks entity version for optimistic locking.
Retry Schedule
When a webhook delivery fails, eRS retries it up to 3 times (4 attempts in total). Each retry is processed as soon as the message is handled. Retries stop when the maximum number of attempts is reached or the webhook is auto-disabled by eRS.
6. Failure Handling & Auto-Disable
Failure Tracking
eRS tracks webhook failures through multiple metrics:
- Fail Count: Total number of failed deliveries (never resets)
- Consecutive Fail Count: Current streak of failures (resets on success)
- Failed At: Timestamp of first failure in current streak
Auto-Disable Conditions
A webhook is automatically disabled when any of these conditions are met:
Condition | Threshold | Description |
|---|---|---|
Consecutive Failures | 200 (default) | Webhook disabled when consecutive fail count >= threshold |
Time Gap | 15 days (default) | Webhook disabled when first failure was >= 15 days ago AND minimum attempts threshold is met |
Minimum Attempts | 20 (default) | Auto-disable only occurs after at least 20 failed attempts |
7. Constraints & Validations
Webhook Constraints
Constraint | Value | Description |
|---|---|---|
Max Webhooks | 5 | Per account |
URL Format | HTTP or HTTPS | Both protocols are accepted |
URL Uniqueness | Required | Each URL can only be used once |
Signing Key Length | 16-64 chars | When encryption enabled |
8. Example Application
Here's a complete example of a webhook receiver that handles both encrypted and unencrypted payloads:
import express from "express";
import http from "http";
import bodyParser from "body-parser";
import crypto from "crypto";
const app = express();
const server = http.createServer(app);
// Use raw body parser to handle encrypted data
app.use(bodyParser.raw({ type: "application/json" }));
const ENCODING = "utf-8";
const ALGORITHM = "aes-256-cbc";
const INIT_VECTOR = "2s5v8y/B?E(H+MbQ"; // 16 bytes for AES
const KEY = "YOUR_ENCRYPTION_KEY"; // Your 32-character hex key
app.post("/", function (req, res) {
const encryptedData = req.body.toString("utf8");
try {
// Try parsing as JSON first (unencrypted payload)
let jsonData;
try {
jsonData = JSON.parse(encryptedData);
console.log("Received unencrypted payload:", jsonData);
// Process the webhook data
processWebhook(jsonData);
return res.status(200).send(jsonData);
} catch (jsonError) {
// If JSON parsing fails, attempt decryption
console.log("JSON parse failed, proceeding with decryption.");
}
// Decrypt the data
console.log("Encrypted data received:", encryptedData);
const decryptedData = decrypt(encryptedData, KEY, INIT_VECTOR);
console.log("Decrypted data:", decryptedData);
// Parse decrypted JSON
jsonData = JSON.parse(decryptedData);
// Process the webhook data
processWebhook(jsonData);
res.status(200).send(decryptedData);
} catch (error) {
console.error("Error processing webhook:", error);
// Return 500 to trigger retry, or 400 for client errors
res.status(500).send("Webhook processing failed");
}
});
function decrypt(encryptedText, key, iv) {
// Convert the key from hex to bytes
const keyBytes = Buffer.from(key, "hex");
// Convert the IV from string to bytes
const ivBytes = Buffer.from(iv, ENCODING);
// Create a decipher
const decipher = crypto.createDecipheriv(ALGORITHM, keyBytes, ivBytes);
// Decrypt the text (assuming base64 encoded input)
let decrypted = Buffer.concat([
decipher.update(Buffer.from(encryptedText, "base64")),
decipher.final(),
]);
// Convert decrypted bytes to string
return decrypted.toString(ENCODING);
}
function processWebhook(data) {
// Your business logic here
console.log("Processing webhook for entity:", data.name || data.title);
console.log("Event type:", data._eventType); // If included in payload
console.log("Entity ID:", data._id);
// Example: Update external system, send notifications, etc.
}
server.listen(process.env.PORT || 3000, function () {
console.log("Express server listening on port 3000.");
});9. Best Practices
Webhook Design
- Use HTTPS: Recommended to use encrypted connections for security
- Idempotent Endpoints: Handle duplicate deliveries gracefully
- Fast Responses: Respond within 5 seconds
- Async Processing: Process webhooks asynchronously when possible
- Logging: Log all webhook deliveries for debugging
Security
- Enable Encryption: Use Encryption Payload for sensitive data
- Validate Payloads: Verify payload structure before processing
- Rate Limiting: Implement rate limiting on your endpoint
- Authentication: Consider additional authentication layers
10. Troubleshooting
Common Issues
Issue 1: Webhook Not Firing
Symptoms: Events occur but webhook doesn't receive notifications
Possible Causes:
- Webhook status is false.
- Trigger status is false.
- Filter conditions not met
- Webhook is system-disabled
Solutions:
- Check webhook status
- Verify trigger is active
- Review filter conditions
- Check webhook logs for errors
Issue 2: URL Verification Fails
Symptoms: Cannot create/update webhook, verification error
Solutions:
Test endpoint manually with curl:
curl -X POST https://your-endpoint.com \ -H "Content-Type: application/json" \ -d '{"challenge":"test-uuid"}'- Ensure endpoint returns the same challenge
- Check firewall/network rules.
- Verify SSL certificate is valid.
Document Version: 1.0
eRS version 4.17.0.244