Guide to Egnyte Webhooks: Features and Best Practices
Egnyte webhooks push activity from an Egnyte domain to your own service in near real time. When someone uploads a file, moves a folder, shares a link, changes a permission or approves a workflow task, Egnyte can tell your application straight away, which is what you need to keep a search index or local cache in sync, log access changes for compliance, or start a business process when a document lands in a folder.
This guide covers how Egnyte webhooks are registered and filtered, the batched payload and event types, how to authenticate requests with the authHeader you set, Egnyte's strict response and retry rules, and the practices that make an Egnyte receiver reliable.
What are Egnyte webhooks?
Egnyte webhooks are managed through the Webhooks API, at https://{domain}.egnyte.com/pubapi/v1/webhooks. Your application registers a webhook with an OAuth 2.0 token that includes the Egnyte.webhooks scope, naming the HTTPS URL to call, the event types it wants and, optionally, the folder paths to watch. Egnyte then sends an HTTP POST to that URL whenever a matching event happens in the domain. Webhooks are the push counterpart of the Events API, which you would otherwise have to poll.
Each request body is an object with a data array, so one delivery can carry more than one event. Every item names its own eventType.
Egnyte does not sign webhook requests. When you register a webhook you can set an authHeader, and Egnyte sends that value as the authorization header on every delivery. Egnyte asks you to validate it on every incoming request.
Egnyte webhook features
| Feature | Details |
|---|---|
| Management | Webhooks API: register (POST), list, details, status, update (PUT), delete |
| API authentication | OAuth 2.0 Bearer token with the Egnyte.webhooks scope |
| Event filtering | Exact names or category wildcards (fs:*, link:*, comment:*, permission:*, meta:*). Omitting eventType subscribes to all events |
| Path filtering | Up to 100 comma-separated folder paths. Omitting path watches every path |
| Limits | The number of webhooks per client ID and domain is limited. Each registration has an expires timestamp |
| Delivery | POST, Content-Type: application/json, body {"data": [...]} |
| Authentication | authorization header set to your registered authHeader, verbatim |
| Signature and timestamp header | None |
| TLS | HTTPS with a valid CA-signed certificate. Self-signed certificates are rejected |
| Success | Exactly HTTP 200 |
| Retries | Immediately, then after 1 minute, 10 minutes and 1 hour (4 attempts). Then the webhook is disabled |
| Source IPs | Not published |
Common events
Egnyte documents 36 event types in seven categories. Wildcards exist for File System, Links, Comments, Permissions and Metadata. Workflow and group events must be named individually.
| Category | Events |
|---|---|
File System (fs:*) | fs:add_file, fs:add_folder, fs:upload_link, fs:copy_file, fs:copy_folder, fs:delete_file, fs:delete_folder, fs:delete_from_trash, fs:move_file, fs:move_folder, fs:restore_from_trash, fs:lock_file, fs:unlock_file, fs:folder_ |
Links (link:*) | link:create, link:create_, link:create_upload_link, link:delete, link:delete_, link:delete_upload_link |
Comments (comment:*) | comment:set_comment, comment:remove_comment |
Permissions (permission:*) | permission:permission_ |
Metadata (meta:*) | meta:add_metadata_key, meta:delete_metadata_key |
| Workflows | workflow:created, workflow:completed, workflow:approvaltask_, workflow:approvaltask_ |
| Groups | group:create, group:delete, group:rename, group:add_members, group:remove_members, group:add_owner, group:remove_owner |
Egnyte lists further workflow events, such as workflow:edited and workflow:cancelled, as planned, and user:create, user:delete, user:disable and user:enable as implemented but not yet supported in v1.
A delivery looks like this. Egnyte's own example uses ... for most values:
{
"data": [
{
"eventId": "...",
"domain": "...",
"timestamp": 1710000000000,
"user": {
"id": 1,
"displayName": "...",
"username": "...",
"email": "..."
},
"actionSource": "...",
"eventType": "fs:add_file",
"webhookId": "...",
"data": {}
}
]
}
The common fields on every item:
| Field | Notes |
|---|---|
eventId | Unique per Egnyte domain. Use it for deduplication |
domain | The Egnyte domain where the event happened |
timestamp | Unix time in milliseconds |
user | id, displayName, username, email, plus optional clientIdHash (SHA-1 of the OAuth client ID that caused the event) and impersonatedBy |
actionSource | The API or client used to perform the action |
eventType | The most specific name for the event |
webhookId | The registration that matched |
customProperties | Optional, such as PROJECT_ID and fileUpdated |
data | Event-specific fields |
The event-specific data varies by type. fs:add_file carries targetEntryId, targetGroupId, targetFileChecksum, targetPath and itemType (file or version). fs:move_file adds sourcePath. permission:permission_ carries folderPath, newPrivilege, oldPrivilege and assignerId, plus nullable assignee, assigneeId, assigner and groupName. Workflow events include a workflow object and a firstEntity object describing the file.
Inspect Egnyte webhooks as they arrive. Register a webhook against a Hookdeck Inspect URL and upload a file to see a real
dataarray, with no account or setup required.
Setting up Egnyte webhooks
Egnyte documents webhook registration through the API. You need an OAuth access token for your Egnyte domain with the Egnyte.webhooks scope. Generate an authHeader value first, for example Bearer followed by the output of openssl rand -hex 32.
curl -X POST "https://{domain}.egnyte.com/pubapi/v1/webhooks" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com/webhooks/egnyte",
"eventType": ["fs:add_file", "fs:delete_file", "fs:move_file"],
"path": "/Shared/Documents/Invoices",
"authHeader": "Bearer your-long-random-token"
}'
The Authorization header on this call is your OAuth token for the API. The authHeader in the body is the separate secret Egnyte will send back to your endpoint. A 201 response returns the webhookId, the expires timestamp and a status of enabled.
authHeader is optional in the API, but without it Egnyte sends nothing you can check, so always set it. To change the URL, events, paths or authHeader later, call PUT /pubapi/v1/webhooks/{webhookId} with the full configuration. GET /pubapi/v1/webhooks/{webhookId}/details returns the current settings, including authHeader.
With Hookdeck. Create a Hookdeck source with the Egnyte type and register its URL as the webhook url. The source has one field, Authorization Header: paste the authHeader value exactly as you registered it, including any Bearer prefix. The source compares the whole header with that value and accepts POST.
For local development, the Hookdeck CLI gives you a public HTTPS URL with a valid certificate: hookdeck listen 3000 egnyte --path /webhooks/egnyte.
Securing Egnyte webhooks
Compare the whole authorization header with the authHeader you registered, in constant time. Egnyte sends the value verbatim, so don't strip a prefix or decode anything.
const crypto = require('crypto');
const express = require('express');
// Exactly the authHeader you registered, e.g. "Bearer 7c1e...".
const EXPECTED = process.env.EGNYTE_AUTHORIZATION;
function safeEqual(received, expected) {
const a = Buffer.from(received, 'utf8');
const b = Buffer.from(expected, 'utf8');
// timingSafeEqual throws on unequal lengths, so check first.
return a.length === b.length && crypto.timingSafeEqual(a, b);
}
const app = express();
app.post('/webhooks/egnyte', express.raw({ type: '*/*' }), (req, res) => {
const received = req.get('authorization');
if (!EXPECTED || typeof received !== 'string' || !safeEqual(received, EXPECTED)) {
return res.sendStatus(401);
}
let body;
try {
body = JSON.parse(req.body.toString('utf8'));
} catch {
return res.sendStatus(400);
}
for (const event of Array.isArray(body.data) ? body.data : []) {
// Deduplicate on event.eventId, then route on event.eventType.
}
// Egnyte treats anything other than 200 as a failure.
res.sendStatus(200);
});
The same check in Python:
import hmac
import os
EXPECTED = os.environ.get("EGNYTE_AUTHORIZATION", "")
def verify_egnyte_request(headers) -> bool:
if not EXPECTED:
return False # never accept when unconfigured
received = headers.get("authorization")
if not received:
return False
# Compare bytes: compare_digest raises TypeError on a non-ASCII str.
return hmac.compare_digest(received.encode("utf-8"), EXPECTED.encode("utf-8"))
A static header proves the sender knows the secret. It does not cover the body, and there is no signed timestamp, so a captured request can be replayed and still pass. Serve the endpoint over HTTPS only (Egnyte requires it anyway), use a long random authHeader, deduplicate on eventId so a replayed event does nothing, and rotate the value with PUT if it might have leaked.
Make Egnyte webhooks production-ready. Hookdeck Event Gateway checks the
authHeaderon every request, answers Egnyte with the200it expects, and queues every event durably so a slow or failing service never gets your webhook disabled.
Egnyte webhook limitations and pain points
Four failures and the webhook is disabled
The Problem: An outage of an hour or so, or a bad deploy that returns errors, doesn't just lose events. It switches the webhook off, and nothing more arrives until someone notices.
Why It Happens: Egnyte retries a failed delivery immediately, then after 1 minute, 10 minutes and 1 hour. After the fourth attempt the registration is disabled.
Workarounds:
- Return
200as soon as the header checks out and process afterwards. - Monitor
GET /pubapi/v1/webhooks/{webhookId}/statusand alert when it readsdisabled. - Re-enable with
POST /pubapi/v1/webhooks/{webhookId}/status, then backfill the gap from the Events API.
How Hookdeck Can Help: Hookdeck answers Egnyte with a 200 as soon as it has stored the request, so your own outages no longer count against Egnyte's four attempts. Delivery to your service then uses retries you configure, and failed events can be replayed once you've fixed the problem.
Only 200 counts
The Problem: A handler that returns 202 Accepted or 204 No Content is treated as failing, and after four of those the webhook is disabled.
Why It Happens: Egnyte requires exactly HTTP 200. Any other status schedules a retry.
Workarounds:
- Return exactly
200from your webhook route, even for events you ignore. - Don't return
4xxfor event types you don't handle. Narrow theeventTypelist instead.
How Hookdeck Can Help: Hookdeck returns 200 to Egnyte for every request it receives, so your service's status codes only affect Hookdeck's delivery to you.
A wrong authHeader disables the webhook too
The Problem: A rotation done in the wrong order, or a missing Bearer prefix on one side, makes your receiver return 401, and four of those disable the registration.
Why It Happens: Egnyte counts a 401 like any other non-200 response.
Workarounds:
- Rotate by accepting both the old and new values in your receiver, then calling
PUTwith the newauthHeader, then removing the old value. - Compare
GET .../detailsagainst your stored value when deliveries start failing.
How Hookdeck Can Help: The Egnyte source answers with a 200 even when the header is wrong, so a mismatch doesn't disable the webhook. The request is recorded as failed verification and never reaches your service, which makes the mistake visible without losing the registration.
Batched events in one request
The Problem: A handler that reads one event per request silently drops the rest.
Why It Happens: The body is {"data": [...]}, and one delivery can carry several events, each with its own eventType.
Workarounds:
- Always loop over
data. - Deduplicate and process each item on its own, so one bad item doesn't fail the batch.
How Hookdeck Can Help: Hookdeck transformations can reshape the batch before it reaches your service, and filters can route deliveries by eventType to different destinations.
Feedback loops from your own changes
The Problem: An integration that writes files back into Egnyte receives webhooks about its own writes, and can end up reacting to itself.
Why It Happens: Every change in a watched path produces an event, whoever made it.
Workarounds:
- Compare
user.clientIdHash, a SHA-1 hash of the OAuth client ID that caused the event, with the hash of your own client ID, and skip matches. - Narrow
pathto the folders you actually need.
How Hookdeck Can Help: A Hookdeck transformation can remove the items whose clientIdHash matches your application before the batch is delivered.
Best practices
Subscribe narrowly
Name the event types and paths you need. Egnyte itself advises against omitting eventType, and a narrow subscription means less traffic and fewer events your handler has to ignore.
Deduplicate on eventId
Retries resend the same events, so store each eventId before acting and skip ones you've seen. See our guide to webhook idempotency.
Monitor each registration's status
Track each webhook's status and expires values. A disabled or expired registration fails silently from your side: your endpoint is healthy but nothing arrives.
Keep the API token and the authHeader separate
The OAuth token that manages webhooks and the authHeader Egnyte sends to you are different secrets. Never reuse the access token as the authHeader.
Acknowledge fast and process asynchronously
Verify the header, store the batch, return 200, and process each event in a background job. See why to process webhooks asynchronously.
Conclusion
Egnyte sends batches of file, link, comment, permission, metadata, workflow and group events as JSON POST requests, authenticated by the authHeader you register and nothing else. Always set authHeader, compare it whole in constant time over HTTPS, loop over data, deduplicate on eventId, and return exactly 200, because four failed attempts disable the webhook.
Hookdeck Event Gateway checks the header, answers Egnyte immediately, and keeps every event until your service has processed it, with retries and replay on your terms.
Get started with Hookdeck for free and handle Egnyte webhooks reliably in minutes.