Webhooks
A webhook is a way for one system to send real-time data to another system as soon as an event happens. Instead of constantly checking (polling) for updates, a webhook lets your application receive updates automatically.
Think of it like a notification system: when something important happens, the system calls your provided URL (usually an API endpoint) with the event data.
How to Use a Webhook
- Provide a URL: You give the system a public URL (your webhook endpoint) that can receive
POST
requests. - Wait for Events: When a specific event occurs (like a person enrichment completing), the system sends a request to your URL with the relevant data.
- Handle the Request: Your server processes the data and responds with a
200 OK
to confirm receipt.
Example Use Case
You want to be notified when a person's enrichment is completed. You provide a webhookUrl
in the request. Once the enrichment is done, we send a POST
request to your URL with the result data.
This approach is efficient, reduces load on your system, and gives you near-instant updates.
Supported event types
Example notification event:
"person.enrichment.completed"
Will be triggered when a contact within a bulk enrichment has been completed.
Source: POST /v2/people/enrich
If the notificationOptions.webhookUrl
field is set, our server will send a POST
request to that URL each time a person enrichment is completed.
To acknowledge successful receipt of the event, your server should respond with an HTTP 200 OK
. If a different status is returned, we may retry the request depending on your configuration.
The request body sent by our server will include the details of the completed person enrichment, as shown below:
Request Body
application/json
RequiredeventType
Requiredstring"person.enrichment.completed"
data
RequiredobjectExample webhook request from our server to your server
Note that the data.person
field is of type EnrichedPersonResponse
, the same type used in the people
field of the GET /v2/people/enrich/:id endpoint.
Example enrichment request with webhookUrl provided
"company.enrichment.completed"
Will be triggered when a company within a bulk enrichment has been completed.
Source: POST /v2/company/enrich
If the notificationOptions.webhookUrl
field is set, our server will send a POST
request to that URL each time a company enrichment is completed.
To acknowledge successful receipt of the event, your server should respond with an HTTP 200 OK
. If a different status is returned, we may retry the request depending on your configuration.
The request body sent by our server will include the details of the completed person enrichment, as shown below:
Request Body
application/json
RequiredeventType
Requiredstring"company.enrichment.completed"
data
RequiredobjectExample webhook request from our server to your server
Note that the data.company
field is of type EnrichedCompanyResponse
, the same type used in the company
field of the GET /v2/company/enrich/:id endpoint.
Example enrichment request with webhookUrl provided
Secure your endpoint
You should secure your webhook integration and verify that all webhook requests are coming from Surfe. You can verify webhook signatures using your webhook shared secret key. You can find your secret key in the API settings. The key becomes available once you’ve received your first webhook notification.
To help add to the security of webhook communication, usage of https endpoints is required.
❌ Not valid, insecure http URL:
✅ Valid, secure https URL:
How to Verify Surfe Webhook Signatures
To ensure webhook requests are genuinely sent by Surfe and have not been tampered with, each webhook event includes an x-surfe-signature
header.
The signature contains 2 parts, a timestamp prefixed with t=
and the signature scheme prefixed by v0=
.
Surfe generates signatures using hash-based message authentication code HMAC with SHA-256.
Follow these steps to verify the signature:
Step 1: Extract timestamp and signature from the header
Split the header using the ,
character as the separator to get a list of elements. Then split each element using the =
character as the separator to get a prefix and value pair.
The value for the prefix t
corresponds to the timestamp, and v0
corresponds to the signature. You can discard all other elements.
Step 2: Recreate the signed payload
The signed_payload
string is created by concatenating:
- The timestamp (as a string)
- The character
.
- The actual JSON payload (that is, the request body)
Step 4: Compute the HMAC Hash
Compute an HMAC with the SHA256 hash function. Use the endpoint’s signing secret as the key, and use the signed_payload
string as the message.
Step 5: Compare Signatures
Compare the computed signature with the value in the x-surfe-signature header. For an equality match, compute the difference between the current timestamp and the received timestamp, then decide if the difference is within your tolerance.
Step 6: Accept or Reject the Request
If the signatures match, process the webhook.
Code examples
Python
Javascript
Golang
Preventing replay attacks
A replay attack occurs when someone captures a legitimate webhook payload and its signature, then tries to resend (replay) it to your endpoint. To help prevent this, Surfe adds a timestamp to the x-surfe-signature
header. Since the timestamp is included in the data that is signed, any attempt to alter it will break the signature verification. If you receive a valid signature but the timestamp is outside your allowed time window, your application should reject the request as potentially malicious.