Developer Tools
Webhooks
Webhook Events are notifications triggered by specific operations to which you can subscribe, allowing you to be alerted when those operations occur. These events might include scenarios such as a Network Token being updated, a successful 3DS session, or the completion of a function deployment. Each event is categorized by a type that defines the nature of the event, along with associated data relevant to that event.
Webhook Event Object
When an event is triggered, Evervault creates a new Event object, which is sent to your Webhook Endpoint as a JSON payload. This payload includes the following fields:
- id: A unique identifier for the event.
- type: The type of event that occurred (e.g.
payments.network-token.updated
orpayments.3ds-session.success
). - data: This field contains the event-specific details.
- createdAt: An epoch millisecond timestamp marking when the event was generated.
Example Event Object
The following example Event Object is triggered when an update occurs on a Network Token:
1{2 "id": "webhook_event_0aa6ff0fee57",3 "type": "payments.network-token.updated",4 "data": {5 "id": "network_token_123456789098",6 "number": "4242424242424242",7 "expiry": {8 "month": "10",9 "year": "27"10 },11 "card": {12 "lastFour": "6401",13 "expiry": {14 "month": "01",15 "year": "29"16 }17 },18 "paymentAccountReference": "71288989897319237219",19 "tokenRequestorIdentifier": "40020248564",20 "tokenServiceProvider": "vts",21 "merchant": "merchant_a1ef231b7947",22 "status": "active",23 "createdAt": 1709293284000,24 "updatedAt": 171163319468825 },26 "createdAt": 171163319469027}
Webhook Event Delivery
Webhook Events are delivered to a Webhook Endpoint via a POST request over HTTPS. The request includes a Content-Type
header set to application/json
. A Webhook Event is deemed successfully delivered when the Webhook Endpoint responds with a 2XX HTTP status code.
Single delivery is not guaranteed, and in rare cases, a Webhook Event may be delivered more than once. To handle potential duplicates, you can use the id
field in the event payload to identify and filter out duplicate events.
You can register a Webhook Endpoint either through the Evervault dashboard (Settings Section) or via the Evervault API. An Evervault App can have multiple Webhook Endpoints registered, with each endpoint subscribed to a specific set of events.
Retries
If the Webhook Endpoint does not respond with a 2XX status code, Evervault will automatically retry sending the event using an exponential backoff strategy, gradually increasing the delay between each retry. This process continues until the event is successfully delivered or until 5 days (120 hours) have passed. If the event cannot be delivered within this timeframe, it will be permanently discarded.
Monitoring
You can monitor Webhook Event deliveries through the Evervault Dashboard, under the Logs tab. The logs include detailed information about each event, such as timestamps and status codes, enabling you to quickly diagnose and address delivery issues.
Getting Started
1. Deploy a new API Endpoint
To start receiving Webhook Endpoints, you need to set up an API endpoint within your infrastructure that can handle POST HTTP requests and process JSON objects. This endpoint must return a 2XX HTTP status code upon successfully completing the event processing.
1const express = require('express');2const app = express();34app.use(express.json());56// Webhooks endpoint7app.post('/evervault', (req, res) => {8 const webhookEvent = req.body;910 switch (webhookEvent.type) {11 case 'payments.network-token.updated':12 // Process the Network Token update13 break;14 // Handle other event types15 default:16 console.log(`Unhandled event type ${event.type}`);17 }18 19 // Respond with HTTP status 20020 res.status(200).send({success: true});21});2223// Start the server24app.listen(3000, () => console.log('Running on port 3000'));
In this example, we are creating a new endpoint, https://hooks.acme.com/evervault
, specifically designed to handle Network Token updates.
2. Secure your Webhook Endpoint
All Webhook Event requests are signed using a secret key, allowing you to verify that the event was sent by Evervault. Each request includes a JSON Web Token (JWT) in the X-Evervault-Signature
header, which you can use to authenticate the request. To do this, compare the signature provided in the header with a signature you generate locally using the payload and endpoint URL.
The signature is created by generating a SHA-256 hash of the body of the request. This hash, along with the endpoint URL, is then signed using the ES256 algorithm (ECDSA with a P-256 curve) and a private key. To verify this signature, use the corresponding public key, which you can retrieve from Evervault’s JSON Web Key Set (JWKS) endpoint.
Below are examples of how to verify an Evervault webhook signature using Node.js, Python, Go and Ruby. For optimal performance, it’s recommended to cache the JWKS instead of fetching it with each request.
1const { createRemoteJWKSet, jwtVerify } = require('jose');2const crypto = require('crypto');34// Function to verify Evervault webhook5async function verifyWebhook(req) {6 try {7 // Convert the request body to a JSON string8 const jsonStr = JSON.stringify(req.body);910 // Retrieve the JWT signature from the request headers11 const jwt = req.headers['X-Evervault-Signature'];1213 // Reconstruct the SHA-256 hash of the request body14 const reconstructedBodySha256 = crypto.createHash('sha256').update(jsonStr).digest('base64');1516 // Reconstruct the full endpoint URL17 const reconstructedEndpointUrl = `${req.protocol}://${req.get('host')}${req.originalUrl}`;1819 // Fetch the JWKS (JSON Web Key Set) for webhook verification20 const JWKS = createRemoteJWKSet(new URL('https://api.evervault.com/jwks/webhooks.json'));2122 // Verify the JWT using the JWKS23 const { payload } = await jwtVerify(jwt, JWKS);24 const { bodySha256, endpointUrl } = payload;2526 // Validate the reconstructed body hash and endpoint URL against the JWT payload27 if (reconstructedBodySha256 !== bodySha256 || reconstructedEndpointUrl !== endpointUrl) {28 throw new Error('Body or endpoint URL could not be verified');29 } else {30 console.log('Webhook verified!');31 }32 } catch (error) {33 console.error('Webhook verification failed.', error);34 throw error;35 }36}3738// Example usage of the verifyWebhook function39await verifyWebhook(req);
1import json2import hashlib3import requests4import base645from jwt import decode6from jwt.exceptions import InvalidTokenError7from cryptography.hazmat.primitives.asymmetric import ec8from cryptography.hazmat.primitives import serialization910def verify_webhook(req):11 try:12 # Convert the request body to a JSON string13 json_str = json.dumps(req.json, separators=(',', ':'))1415 # Retrieve the JWT signature from the request headers16 jwt_token = req.headers.get('X-Evervault-Signature')1718 # Reconstruct the SHA-256 hash of the request body19 reconstructed_body_sha256 = hashlib.sha256(json_str.encode('utf-8')).digest()20 reconstructed_body_sha256_base64 = base64.b64encode(reconstructed_body_sha256).decode('utf-8')2122 # Reconstruct the full endpoint URL23 reconstructed_endpoint_url = f"{req.scheme}://{req.host}{req.path}".replace('http://', 'https://')2425 # Fetch the JWKS (JSON Web Key Set) for webhook verification26 jwks_url = 'https://api.evervault.io/jwks/webhooks.json'27 jwks_response = requests.get(jwks_url)28 jwks = jwks_response.json()2930 # Extract the kid from the JWT header31 unverified_header = get_unverified_header(jwt_token)32 kid = unverified_header['kid']3334 # Find the correct JWK using the kid35 key = next((k for k in jwks['keys'] if k['kid'] == kid), None)36 if not key:37 raise ValueError('Invalid key ID (kid)')3839 # Convert JWK to PEM format40 x = int.from_bytes(base64.urlsafe_b64decode(key['x'] + '=='), 'big')41 y = int.from_bytes(base64.urlsafe_b64decode(key['y'] + '=='), 'big')42 public_key = ec.EllipticCurvePublicNumbers(x, y, ec.SECP256R1()).public_key()43 pem = public_key.public_bytes(44 encoding=serialization.Encoding.PEM,45 format=serialization.PublicFormat.SubjectPublicKeyInfo46 )4748 # Verify the JWT using the public key49 payload = decode(jwt_token, pem, algorithms=['ES256'])50 body_sha256 = payload['bodySha256']51 endpoint_url = payload['endpointUrl']5253 # Validate the reconstructed body hash and endpoint URL against the JWT payload54 if reconstructed_body_sha256_base64 != body_sha256 or reconstructed_endpoint_url != endpoint_url:55 raise ValueError('Body or endpoint URL could not be verified')56 else:57 print('Webhook verified!')58 return True5960 except (InvalidTokenError, ValueError) as error:61 print('Webhook verification failed.', error)62 return False6364# Example usage of the verify_webhook function65if verify_webhook(req):66 print('Webhook verified!')67else:68 print('Webhook verification failed.')69
1import (2 "crypto/sha256"3 "encoding/base64"4 "encoding/json"5 "fmt"6 "io/ioutil"7 "log"8 "net/http"910 "github.com/MicahParks/keyfunc"11 "github.com/golang-jwt/jwt/v4"12)1314const jwksURL = "https://api.evervault.com/jwks/webhooks.json"1516// WebhookPayload represents the structure of the JWT payload17type WebhookPayload struct {18 BodySha256 string `json:"bodySha256"`19 EndpointUrl string `json:"endpointUrl"`20}2122func verifyWebhook(req *http.Request) (bool, error) {23 // Read and marshal request body to JSON string24 body, err := ioutil.ReadAll(req.Body)25 if err != nil {26 return false, err27 }28 jsonStr, err := json.Marshal(json.RawMessage(body))29 if err != nil {30 return false, err31 }3233 // Reconstruct SHA-256 hash of the body34 hash := sha256.Sum256(jsonStr)35 reconstructedBodySha256 := base64.StdEncoding.EncodeToString(hash[:])3637 // Reconstruct full endpoint URL38 reconstructedEndpointUrl := fmt.Sprintf("%s://%s%s", "https", req.Host, req.URL.Path)3940 // Fetch the JWKS for webhook verification41 jwks, err := keyfunc.Get(jwksURL, keyfunc.Options{})42 if err != nil {43 return false, fmt.Errorf("failed to get JWKS: %w", err)44 }4546 // Retrieve JWT signature from headers47 jwtToken := req.Header.Get("x-evervault-signature")48 if jwtToken == "" {49 return false, fmt.Errorf("missing JWT token")50 }5152 // Decode JWT and verify signature using keys from JWKS53 token, err := jwt.Parse(jwtToken, func(token *jwt.Token) (interface{}, error) {54 // Use the JWKS to fetch the key for verification55 return jwks.Keyfunc(token)56 })57 if err != nil {58 return false, fmt.Errorf("JWT verification failed: %w", err)59 }6061 // Check token validity62 if !token.Valid {63 return false, fmt.Errorf("JWT token is not valid")64 }6566 // Extract claims from the JWT token67 claims, ok := token.Claims.(jwt.MapClaims)68 if !ok {69 return false, fmt.Errorf("JWT claims are not valid")70 }7172 // Extract values from the claims73 bodySha256, ok := claims["bodySha256"].(string)74 if !ok {75 return false, fmt.Errorf("bodySha256 claim is missing or not a string")76 }77 endpointUrl, ok := claims["endpointUrl"].(string)78 if !ok {79 return false, fmt.Errorf("endpointUrl claim is missing or not a string")80 }8182 // Validate the reconstructed body hash and endpoint URL against the JWT payload83 if reconstructedBodySha256 != bodySha256 || reconstructedEndpointUrl != endpointUrl {84 return false, fmt.Errorf("verification failed: hashes or URLs do not match")85 }8687 return true, nil88}
1require 'json'2require 'openssl'3require 'jwt'4require 'net/http'5require 'uri'67def verify_webhook(request_payload, signature, request_url)8 begin9 # Convert the request body to a JSON string10 json_str = JSON.generate(request_payload)1112 # Reconstruct the SHA-256 hash of the request body13 sha256_digest = OpenSSL::Digest::SHA256.new14 sha256_digest.update(json_str)15 reconstructed_body_sha256 = Base64.strict_encode64(sha256_digest.digest)1617 # Reconstruct the full endpoint URL18 reconstructed_endpoint_url = request_url1920 # Fetch the JWKS (JSON Web Key Set) for webhook verification21 jwks_uri = URI.parse('https://api.evervault.com/jwks/webhooks.json')22 jwks_response = Net::HTTP.get(jwks_uri)23 jwks = JSON.parse(jwks_response)24 jwk_set = JWT::JWK::Set.new(jwks['keys'])2526 # Verify the JWT using the JWKS27 decoded_token = JWT.decode(signature, nil, false, jwks: jwk_set)28 payload = decoded_token[0]2930 body_sha256 = payload['bodySha256']31 endpoint_url = payload['endpointUrl']3233 # Validate the reconstructed body hash and endpoint URL against the JWT payload34 if reconstructed_body_sha256 != body_sha256 || reconstructed_endpoint_url != endpoint_url35 raise 'Body or endpoint URL could not be verified'36 else37 puts 'Webhook verified!'38 true39 end40 rescue => e41 puts "Webhook verification failed. #{e.message}"42 false43 end44end
3. Register your Webhook Endpoint with Evervault
After deploying your new endpoint, you can register it as a Webhook Endpoint with Evervault through either the Dashboard (Settings > Webhooks) or the Evervault API. You’ll need to subscribe this endpoint to the specific events you wish to receive. Once subscribed, Evervault will send those events to the registered endpoint whenever they occur for your app.
In this example, we are registering the newly created endpoint, https://hooks.acme.com/evervault
, to listen for the payments.network-token.updated
event.
Webhook Event Samples
Function Webhooks
View sample webhook events for asynchronous Function invocations and new deployments.
Get Started