Get notified about updates
A webhook is an HTTP request used to provide notifications to your System when changes happen in Dotfile.
Dotfile sends webhooks to your server to notify programmatically about:
- Case lifecycle
- Case created
- Case updated
- Case status updated
- Case flags updated
- Case info updated
- Case template updated
- Case risk updated
- Case metadata updated
- Case tags updated
- Case review updated
- Case review due
- Case review confirmed
- Case deleted
- Case report lifecycle
- Case report generated
- Note lifecycle
- Note created
- Note updated
- Note deleted
- Note comment lifecycle
- Note comment created
- Note comment updated
- Note comment deleted
- Individual lifecycle
- Individual created
- Individual updated
- Individual deleted
- Company lifecycle
- Company created
- Company updated
- Company deleted
- Check lifecycle
- Check started
- Check review needed
- Check approved or rejected
- Check expired
- Check deleted
- Document order lifecycle
- Document order completed
- Document order failed (i.e. document not available)
Configuring webhooks
To receive webhooks, set up a dedicated endpoint on your server and then set up webhook in Dotfile using our API. Dotfile sends POST requests with a raw JSON payload to your designated endpoint.
To create a webhook using the API, use the Create a webhook operation.
Events
Deleted
events does not trigger the deleted event for sub-entity.Example: Case deletion will trigger a
Case.Deleted
event, but noIndividual.Deleted
,Company.Deleted
norCheck.Deleted
event.
Case events
Case.Created
: When a new case is created (empty case).Case.Updated
: When any case property (such asname
,external_id
,status
,flags
,risk
,tags
,template_id
,metadata
,custom_properties
) is updated.Case.StatusUpdated
: When case status is updated from one status to another one.
Example: caseopen
βapproved
Case.FlagsUpdated
: When case flags are updated (any flag changes can trigger this event).Case.InfoUpdated
: When case info are updated (such asname
,external_id
orcustom_properties
).Case.TemplateUpdated
: When case template is updated.Case.RiskUpdated
: When case current Risk is updated.Case.MetadataUpdated
: When case metadata are updated.Case.TagsUpdated
: When tags have been updated on case.
Case.ReviewUpdated
: When the case periodic review is updated (for example when the caseβs risk changes and thenext_review_at
date is updated).Case.ReviewDue
: When the case periodic review is due.Case.ReviewConfirmed
: When the case periodic review is confirmed.Case.Deleted
: When a case is deleted.
Case report events
CaseReport.Generated
: When a case report link is generated.
Note events
Note.Created
: When a new note is created.Note.Updated
: When any note content is updated.Note.Deleted
: When a note is deleted.
Note comment events
NoteComment.Created
: When a new note comment is created.NoteComment.Updated
: When any note comment content is updated.NoteComment.Deleted
: When a note comment is deleted.
Individual events
Individual.Created
: When a new individual is created.Individual.Updated
: When any individual property (not check related) is updated.
Example:risk
property is updatedIndividual.Deleted
: When a individual is deleted.
Company events
Company.Created
: When a new company is created.Company.Updated
: When any company property (not check related) is updated.
Example:risk
property is updatedCompany.Deleted
: When a company is deleted.
Check events
-
Check.Started
: When a check is started on an entity.
Example: Start an AML Check on Individual. -
Check.ReviewNeeded
: When a check needs manual review.
Example: Found hits on AML Check, a reviewer needs to look at the hits. -
Check.Approved
: When a check is approved.
Example: Ignore all hits (false-positive), the reviewer approves the AML check which trigger the webhook on this event. -
Check.Rejected
: When a check is rejected.
Example: A document is invalid, the reviewer rejects the Document check which trigger the webhook on this event. -
Check.Expired
: When a check is expired.
Example: A document check expiration date has been past, the check become expired. -
Check.Deleted
: When a check is deleted.
Example: A document check has been removed by a user.
Document order events
DocumentOrder.Completed
: When a document order for a company has been completed .
Example: KBIS for a french company has been retrieved.DocumentOrder.Failed
: When a document order for a company has failed.
Example: Unavailable annual accounts for a company.
Events can have sub events for fine-grain filtering.
For instance, the top-level event
Case.Updated
has one sub-eventCase.StatusUpdated
that allow you to subscribe specifically to the update of the casestatus
and ignore update of other properties.You should note that the top-level event is always trigger and you donβt need to subscribe to a sub-event if you are already subscribe to the top-level event in the same webhook.
Payload
Payload structure:
{
"event": "ENTITY_NAME.ACTION_NAME",
"context": { ... },
"ENTITY_NAME": { ... }
}
Context
You can find useful extra properties in the context such as:
timestamp
a UNIX timestamp of the time when the webhook is processed. You can verify that this timestamp is within 1-2 minutes of the time your system receives it to prevent replay attacks.event_id
UUID of the event. When a webhook is retried the event id is the same as the initial call.retry_count
Webhook retry counter. 0 for the initial call.remaining_retry_count
Webhook automatic remaining retry counter.
The context
also contains additional properties depending on the entity targeted by the webhook.
Event | context properties |
---|---|
Case.* | workspace |
CaseReport.* | workspace , case |
Note.* | workspace, case |
NoteComment.* | workspace, case, note |
Individual.* | workspace, case |
Company.* | workspace, case |
Check.* | workspace, case, individual, company |
DocumentOrder.* | workspace |
workspace
context contains a workspace subset with itsid
andname
.case
: contains a case subset with itsid
,external_id
,name
,tags
,contact_has_actions
,reviewer_has_actions
,flags
,metadata
andstatus
.note
: contains a note subset with itsid
,author.id
,author.first_name
,author.last_name
,author.email
,content
individual
context contains an individual subset withid
,first_name
,last_name
company
context contains an individual subset withid
,name
,country
The context can be useful to verify that the webhook comes from a specific workspace or build URL to the Console App.
You can see all payload definition documented in the Callbacks section (latest section of Create a webhook).
How to test webhook
When integrating a webhook, you need to provide the URL that will be called.
To quickly inspect the payload, you can use webhook.site. It will generate a unique URL and you will see the incoming HTTP Request in your browser.
To start handling webhook locally, you can use ngrok. It will generate a unique URL to expose your local machine to the internet. You can create a webhook with this URL and the incoming request will be forwarded to your machine.
> ngrok http http://localhost:4000/
# will generate an URL like https://0ebb-92-8-30-12.eu.ngrok.io
Securing webhook
We support securing webhooks through content hashing with a signature. A SHA256 HMAC signature is calculated for the content and delivered in the Dotfile-Signature
header, which can be used for comparison.
We also support secret rotation with no downtime.
If you rotate your secret, we will provide a second header
Dotfile-Old-Signature
for you to validate against your old secret. See below example for more insight.
To verify a webhook, calculate the signature from the request body using the webhook secret. It is recommended to use the raw request body content for hashing, as using JSON parsing may alter it.
Here is an example of minimal express server implementation:
import express from 'express';
import bodyParser from 'body-parser';
import { createHmac } from 'crypto';
const app = express();
const port = 3000;
// Replace with your webhook secret
const WEBHOOK_SECRET = 'dotsecret.XXXXXXXXXX';
// Add middleware to extract raw request body
app.use(
express.json({
verify: (req, res, buf) => {
req.rawBody = buf.toString();
},
})
);
// Parse the request body
app.use(bodyParser.json());
// Receive HTTP POST requests
app.post('/dotfile-webhook', (req, res) => {
const payload = req.body;
const rawBody = req.rawBody;
// Verify signature
const signature = createHmac('sha256', WEBHOOK_SECRET)
.update(rawBody)
.digest('hex');
let isSignatureValid = false;
if (signature === req.headers['dotfile-signature']) {
isSignatureValid = true;
console.log('β
Valid signature');
}
if (signature === req.headers['dotfile-old-signature']) {
isSignatureValid = true;
console.log('β
Valid signature but has been rotated, update your config');
}
if (!isSignatureValid) {
res.sendStatus(400);
console.error('Invalid signature');
return;
}
// Do something neat with the data received!
console.log(payload);
// Finally, respond with a HTTP 200 to signal all good
res.sendStatus(200);
});
app.listen(port, () =>
console.log(`Webhook consumer listening on port ${port}!`)
);
We also include a UNIX timestamp of the time when the webhook is processed in context.timestamp
. You can verify that this timestamp is within 1-2 minutes of the time your system receives it to prevent replay attacks.
Failure and retry
We expect a 2XX
response code from your endpoint when we call your webhook URL. If we receive any other response code, we will interpret it as a failure and retry the webhook with an exponential backoff until we receive a 2XX
response code.
We will retry 4 times after the initial call, respectively 1 hour, 4 hours, 13 hours and 40 hours after each failure. For instance:
- 1st automated retry: 1 hours after the initial call
- 2nd automated retry: 4 hours after the initial call (3 hours after the previous automated retry)
- 3rd automated retry: 13 hours after the initial call (9 hours after the previous automated retry)
- 4th automated retry: 40 hours after the initial call (27 hours after the previous automated retry)
If you modify your endpoint, our retry mechanism will take that into account and update the URL accordingly.
You can find retry information in the payload context or in request headers
context.event_id
or headerdotfile-event-id
- UUID of the event. When a webhook is retried the event id is the same as the initial call.context.retry_count
or headerdotfile-retry-count
- Webhook retry counter. 0 for the initial call.context.remaining_retry_count
or headerdotfile-remaining-retry-count
- Webhook automatic remaining retry counter. Max retry count: 4.
You can see and retry a webhook call manually from our Console App.
If a manual retry is successful for a given webhook call, there will be no subsequent automatic retry.
If a manual retry is unsuccessful, it will count as a retry against the automatic retry count.
Next automated retry, if any, will be done according to the interval against the initial call.
Webhook automatically set offline
If your webhook has more than 50 calls in failure within a rolling window of 24h, it will automatically set your webhook offline. You can update the URL and set back your webhook online via API or Console App to restart it.