Skip to main content

Webhooks API

Get instant notifications when your e-manage data changes. This guide will help you set up and manage webhooks to keep your systems in sync with e-manage in real-time.

What are Webhooks?

Webhooks notify your application immediately when something changes in your e-manage database. Instead of constantly checking for updates, e-manage automatically sends you the changes as they happen.

Example: When a project's status changes from "In Progress" to "Completed," we'll send a notification to your system within seconds.


Quick Start

Step 1: Get Your API Key

Contact your e-manage administrator or support team to get your API key. You'll need this to manage your webhook subscriptions.

Important: Keep your API key secure - treat it like a password.

If you have an API key already, please contact support to enable webhooks for your company

Step 2: Create Your First Webhook

Tell e-manage where to send notifications and what changes you want to know about.

API Endpoint:POST https://emanageone.azure-api.net/emws/v1/webhooks

What to send:

{
"TableName": "Projects",
"Events": ["Insert", "Update"],
"CallbackUrl": "https://your-website.com/webhooks"
}

Include your API key in the header:

x-api-key: your-api-key-here

What you'll receive back:

{
"Id": "a4eaf4a5-8798-4cd0-8e64-2e84a12a0a61",
"Secret": "sec_2wAd...gK"
}

Save the Secret! You'll need it to verify that notifications are actually from e-manage. We only show it once for security reasons.

Step 3: Test Your Webhook

Before going live, send yourself a test notification to make sure everything works.

API Endpoint:POST https://emanageone.azure-api.net/emws/v1/webhooks/{webhookId}/test-event

What to send:

{
"Table": "Projects",
"Operation": "Update",
"Keys": { "ProjectID": 123 },
"Current": {
"ProjectID": 123,
"ProjectName": "Test Project",
"Status": "Completed"
}
}

This sends a fake notification to your endpoint so you can verify your system receives and processes it correctly.


Understanding Webhook Notifications

What You'll Receive

When data changes in e-manage, we'll send a notification to your CallbackUrl via HTTP POST.

Example Notification:

{
"EventId": "6a1d9f86-6f0c-4c1e-97a0-46ab2f6c4f0d",
"CreatedUtc": "2025-08-20T19:05:13.181Z",
"Table": "Projects",
"Operation": "updated",
"Keys": { "ProjectID": 8324 },
"Current": {
"ProjectID": 8324,
"CompanyID": 5132,
"ProjectName": "Website Redesign",
"Status": "Completed"
},
"ChangedColumns": ["Status"]
}

What Each Field Means:

  • EventId: Unique identifier for this notification (useful for preventing duplicates)
  • CreatedUtc: When the change happened (UTC timezone)
  • Table: Which database table changed (e.g., "Projects", "Customers", "Invoices")
  • Operation: What happened - "created", "updated", or "deleted"
  • Keys: The unique identifier(s) for the changed record
  • Current: The current values of the record (will be null if deleted)
  • ChangedColumns: Which fields changed (only for updates)

Security Headers

Every notification includes security headers to prove it's from e-manage:

  • X-Signature: A cryptographic signature (explained below)
  • X-Event-Id: Matches the EventId in the body
  • X-Timestamp: When the notification was sent
  • X-Event-Schema: Version of the notification format (currently "emws.webhook.v1")

Security & Verification

Why Verify Signatures?

Anyone could send fake notifications to your webhook URL. The signature proves the notification is actually from e-manage and hasn't been tampered with.

How to Verify (Simplified)

For Developer Teams:

When you receive a notification:

  1. Read the complete request body (the JSON payload)
  2. Use your secret key to calculate what the signature should be
  3. Compare it to the X-Signature header
  4. Only process the notification if they match

Code Examples:

Node.js:

const crypto = require('crypto');

function isValidSignature(requestBody, secret, signatureHeader) {
// Remove the "sha256=" prefix from the header
const receivedSignature = signatureHeader.replace('sha256=', '');

// Calculate what the signature should be
const expectedSignature = crypto
.createHmac('sha256', secret)
.update(requestBody)
.digest('hex');

// Securely compare them
return crypto.timingSafeEqual(
Buffer.from(expectedSignature, 'utf8'),
Buffer.from(receivedSignature, 'utf8')
);
}

// Use it in your webhook handler
app.post('/webhooks', (req, res) => {
const signature = req.headers['x-signature'];
const rawBody = req.rawBody; // Important: use the raw body, not parsed JSON

if (!isValidSignature(rawBody, YOUR_SECRET, signature)) {
return res.status(401).send('Invalid signature');
}

// Signature is valid - process the webhook
const event = JSON.parse(rawBody);
// ... your business logic here ...

res.status(200).send('OK');
});

C#:

using System.Security.Cryptography;
using System.Text;

bool IsValidSignature(string requestBody, string secret, string signatureHeader)
{
// Remove the "sha256=" prefix
var parts = signatureHeader?.Split('=', 2);
if (parts?.Length != 2 || parts[0] != "sha256") return false;

var receivedSignature = parts[1];

// Calculate what the signature should be
using var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(secret));
var hash = hmac.ComputeHash(Encoding.UTF8.GetBytes(requestBody));
var expectedSignature = BitConverter.ToString(hash)
.Replace("-", "")
.ToLowerInvariant();

// Securely compare them
return CryptographicOperations.FixedTimeEquals(
Encoding.UTF8.GetBytes(expectedSignature),
Encoding.UTF8.GetBytes(receivedSignature)
);
}

Additional Security Best Practices

  1. Check the Timestamp: Reject notifications older than 5 minutes (check X-Timestamp header) to prevent replay attacks
  2. Track Event IDs: Store the EventId values you've processed to avoid processing the same event twice
  3. Use HTTPS: Always use https:// URLs for your CallbackUrl to encrypt notifications in transit

Managing Your Webhooks

All management operations require your API key in the x-api-key header.

List All Your Webhooks

See all webhooks you've created.

API Endpoint:GET https://emanageone.azure-api.net/emws/v1/webhooks

Response:

[
{
"Id": "a4eaf4a5-8798-4cd0-8e64-2e84a12a0a61",
"TableName": "Projects",
"Events": ["Insert", "Update"],
"CallbackUrl": "https://your-website.com/webhooks",
"IsActive": true,
"CreatedAt": "2025-08-20T19:05:13.181Z"
}
]

Get Webhook Details

View details for a specific webhook.

API Endpoint:GET https://emanageone.azure-api.net/emws/v1/webhooks/{webhookId}

Update a Webhook

Change which events you want to receive, update your URL, or pause notifications.

API Endpoint:PUT https://emanageone.azure-api.net/emws/v1/webhooks/{webhookId}

Examples:

Change which events you receive:

{
"Events": ["Insert", "Update", "Delete"]
}

Update your callback URL:

{
"CallbackUrl": "https://your-new-website.com/webhooks"
}

Pause notifications temporarily:

{
"IsActive": false
}

Resume notifications:

{
"IsActive": true
}

You can update multiple fields at once by including them all in the request.

Delete a Webhook

Stop receiving notifications permanently.

API Endpoint:DELETE https://emanageone.azure-api.net/emws/v1/webhooks/{webhookId}

Response: 204 No Content (success with no body)

Rotate Your Secret Key

If you think your secret has been compromised, generate a new one.

API Endpoint:POST https://emanageone.azure-api.net/emws/v1/webhooks/{webhookId}/rotate-secret

What to send:

{
"NewSecret": null
}

(Leave null to have e-manage generate a secure secret for you)

Response:

{
"Secret": "sec_new_7cV...0Q"
}

Important: For a grace period, we'll send both the old and new signatures in notification headers (X-Signature and X-Signature-Old). This gives you time to update your verification code without missing any notifications. After the grace period ends, only the new signature will be sent.


Customizing Webhook Requests

Adding Custom Headers

You can include custom HTTP headers with every notification (useful for authentication or routing).

When creating a webhook:

{
"TableName": "Projects",
"Events": ["Update"],
"CallbackUrl": "https://your-website.com/webhooks",
"HeadersJson": "{\"X-Custom-Auth\":\"abc123\",\"X-Environment\":\"production\"}"
}

Every notification will include your custom headers along with the standard ones.

Choosing Which Events to Track

You can subscribe to any combination of these events:

  • Insert: Notifies when new records are created
  • Update: Notifies when existing records are modified
  • Delete: Notifies when records are deleted

Example: Only track new projects:

{
"Events": ["Insert"]
}

Example: Track all changes:

{
"Events": ["Insert", "Update", "Delete"]
}

Which Tables Can You Track?

You can create webhooks for following tables in your e-manage database:

Companies
CompanyDocuments
CompanyNotes
CompanySalespeople
ContactGroupMembers
ContactGroups
ContactNotes
Contacts
CorporateDivision
Deposits
Employees
EmployeeTime
EventAttendees
EventInvitations
Events
FollowUpRecipients
FollowUps
ImplementationParts
MarketingAnswers
MarketingCampaigns
MarketingQuestions
ProjectContacts
ProjectDocuments
ProjectInstallationInstallers
ProjectInstallationOutSourcing
ProjectInstallationSchedule
ProjectMarketing
ProjectMilestoneAlerts
ProjectMilestones
ProjectNotes
ProjectOrderEntry
ProjectPoitemsAckQty
ProjectPunchListItemDocuments
ProjectPunchListItems
ProjectPurchaseOrderAcknowledgements
ProjectPurchaseOrderItems
ProjectPurchaseOrderItemsAckQtyReceived
ProjectPurchaseOrders
Projects
ProjectSalesPeople
ProjectScope
SalesLeads
SalesTeams
SurveyNames
SystemNoteAlerts
Users
ZipCodes

Ask e-manage support to enable any additional tables for your specific use case.


Delivery & Reliability

How We Deliver Webhooks

  1. Fast Delivery: We send notifications within a minute of a change
  2. Automatic Retries: If your endpoint is temporarily down, we'll keep trying
  3. Retry Schedule: We retry with increasing delays (30 seconds, 1 minute, 2 minutes, 4 minutes, etc.) up to 1 hour between attempts
  4. Guaranteed Delivery: We'll keep trying until you respond with success or we determine the endpoint is permanently unreachable

What You Need to Do

Respond Quickly:

  • Return a 200 OK response within 10 seconds
  • If you need to do heavy processing, accept the webhook first, then process it in the background

Example (Recommended Pattern):

app.post('/webhooks', async (req, res) => {
// 1. Verify signature
if (!isValidSignature(req.body, secret, req.headers['x-signature'])) {
return res.status(401).send('Invalid signature');
}

// 2. Respond quickly
res.status(200).send('OK');

// 3. Process in background (after responding)
processWebhookAsync(req.body).catch(err => {
console.error('Failed to process webhook:', err);
});
});

Handle Duplicates:

  • Due to retries, you might receive the same notification more than once
  • Use the EventId field to detect and ignore duplicates

Signal When to Stop:

  • Return 410 Gone to permanently disable a webhook you no longer want
  • We'll automatically disable webhooks that consistently fail

Monitoring Your Webhooks

e-manage tracks webhook health automatically:

  • Failure Count: How many consecutive failed deliveries
  • Last Failure: When and why the last delivery failed
  • Backoff Status: If we're temporarily delaying deliveries due to repeated failures

If a webhook fails too many times, we may automatically disable it to prevent wasting resources. You can re-enable it by updating IsActive to true.


Common Use Cases

1. Keep an External System in Sync

Scenario: You have a custom dashboard that displays project data from e-manage.

Solution:

{
"TableName": "Projects",
"Events": ["Insert", "Update", "Delete"],
"CallbackUrl": "https://dashboard.yourcompany.com/api/sync"
}

When a project changes in e-manage, your dashboard receives a notification and updates immediately.

2. Send Notifications to Your Team

Scenario: Alert your team via Slack when high-priority projects are created.

Solution:

{
"TableName": "Projects",
"Events": ["Insert", "Update"],
"CallbackUrl": "https://your-middleware.com/project-alerts"
}

Your middleware checks if the project is high-priority and posts to Slack if needed.

3. Trigger Business Workflows

Scenario: Start an approval workflow when an invoice exceeds a certain amount.

Solution:

{
"TableName": "Invoices",
"Events": ["Insert"],
"CallbackUrl": "https://workflow.yourcompany.com/invoice-approval"
}

Your workflow system receives new invoice notifications and triggers approvals based on your business rules.

4. Audit Trail

Scenario: Maintain a detailed history of all changes to customer records.

Solution:

{
"TableName": "Customers",
"Events": ["Insert", "Update", "Delete"],
"CallbackUrl": "https://audit.yourcompany.com/log"
}

Store every change in your audit system for compliance and reporting.


Troubleshooting

My Webhook Isn't Receiving Notifications

Check These Common Issues:

  1. Is the webhook active?
  • Use GET /emws/v1/webhooks/{webhookId} to check IsActive
  • If false, update with {"IsActive": true}
  1. Is your endpoint accessible?
  • Test with the test event endpoint
  • Verify your server is reachable from the internet
  • Check firewall rules and security groups
  1. Are you responding correctly?
  • Must return 200 OK status code
  • Must respond within 10 seconds
  • Check your server logs for errors
  1. Is the webhook in backoff mode?
  • After repeated failures, we temporarily pause deliveries
  • Check the LastFailureAt and BackoffUntilUtc fields
  • Fix the issue and we'll automatically resume

Signature Verification Fails

Common Mistakes:

  1. Using the parsed JSON instead of raw body
  • ✗ Wrong: JSON.stringify(req.body)
  • ✓ Correct: Use the original request body bytes
  1. Wrong secret encoding
  • Secrets are Base64-encoded strings
  • Make sure to decode if your library requires it
  1. Comparing during rotation
  • During secret rotation, check both X-Signature and X-Signature-Old

Getting 403 Forbidden

Error Message: "Your company does not have webhooks feature enabled"

Solution: Contact e-manage support to enable webhooks for your account.

Getting 404 Not Found

The webhook ID doesn't exist or belongs to a different tenant. Double-check the webhook ID from your list of webhooks.


API Reference Summary

Base URL

https://emanageone.azure-api.net/emws/v1

Authentication

All requests require the x-api-key header:

x-api-key: your-api-key-here

Endpoints

MethodEndpointPurpose
POST/webhooksCreate a webhook
GET/webhooksList all webhooks
GET/webhooks/{id}Get webhook details
PUT/webhooks/{id}Update a webhook
DELETE/webhooks/{id}Delete a webhook
POST/webhooks/{id}/rotate-secretRotate secret key
POST/webhooks/{id}/test-eventSend test notification

Error Codes

CodeMeaning
200Success
201Created successfully
204Deleted successfully
400Invalid request (check your data)
401Missing or invalid API key
403Webhooks not enabled for your account
404Webhook not found
422Request cannot be processed
429Too many requests (rate limited)
5xxServer error (contact support if persistent)

Best Practices Summary

  1. Security First
  • Always verify signatures
  • Use HTTPS for your callback URLs
  • Keep your API key and secrets secure
  • Check timestamps to prevent replay attacks
  1. Reliability
  • Respond with 200 OK within 10 seconds
  • Process webhooks in the background
  • Track EventId to handle duplicates gracefully
  • Monitor webhook health in your logs
  1. Efficiency
  • Only subscribe to tables and events you need
  • Return 410 Gone for unwanted webhooks
  • Use custom headers for routing instead of multiple webhooks
  1. Testing
  • Always test with the test-event endpoint before going live
  • Test your signature verification logic
  • Test failure scenarios (what happens if your endpoint is down?)

Versioning

Current Version:emws.webhook.v1

We may add new optional fields to webhook notifications in the future. Your code should ignore fields it doesn't recognize to maintain forward compatibility.

If we need to make breaking changes, we'll introduce a new version (emws.webhook.v2) and give you plenty of time to migrate.


Quick Reference Card

Creating a Webhook

POST https://emanageone.azure-api.net/emws/v1/webhooks
x-api-key: YOUR_KEY

{
"TableName": "Projects",
"Events": ["Insert", "Update"],
"CallbackUrl": "https://your-site.com/webhooks"
}

Receiving a Webhook

// 1. Verify signature
const isValid = verifySignature(
rawBody,
yourSecret,
req.headers['x-signature']
);

// 2. Respond quickly
res.status(200).send('OK');

// 3. Process async
processInBackground(event);

Testing a Webhook

POST https://emanageone.azure-api.net/emws/v1/webhooks/{id}/test-event
x-api-key: YOUR_KEY

{
"Table": "Projects",
"Operation": "Update"
}