Webhook Subscription
Receive real-time event notifications and react instantly to changes in card and transaction activity.
Webhooks allow your application to receive real-time updates when events occur in the system, without needing to continuously poll APIs.
Instead of making repeated API calls (pull), webhooks deliver event data directly to your system (push), making integrations more efficient, scalable, and responsive.
Common use cases include:
- Transaction declines or approvals
- Settlement updates
- Card lifecycle events
How Webhooks Work
Webhooks follow a simple event-driven flow:
- Subscribe to Events Register the events you want to receive and provide a secure HTTPS endpoint
- Event Occurs An event is triggered in the system (e.g., transaction declined)
- Webhook Sent A JSON payload is delivered via HTTP POST to your endpoint
- Process & Acknowledge Your system validates and processes the event
2.1. Setup for Webhook Subscription
Step 1. Navigate to the Webhooks section in the Program Manager portal.
Step 2. Enter the following configuration details:
-
Name:
A user-friendly name to identify your webhook (e.g.,Customer Update Webhook). -
Description:
An optional, brief description of the webhook's purpose. -
URL:
The secure HTTPS endpoint on your server that will receive the webhook notifications. -
Authentication Type:
Choose how you want to secure your endpoint:- Header (Basic Access Authentication): The basic access authentication is a method for an HTTP user agent to provide a username and password when making a request. In basic HTTP authentication, a request contains a header field in the form of Authorization: Basic <credentials>, where <credentials> is the Base64 encoding of username and password joined by a single colon(: ).
Eg: Authorization: Basic U3Ryb25nVXNlck5hbWVAZG9tYWlsbmFtZTpTdHJvbmdQYXNzd29yZA== - Signature Validation (Recommended): A more secure method where each incoming message includes a cryptographic signature that you can verify using a provided public key. This ensures the message is authentic and unaltered.
The public key can be used to verify authenticity for type = subscriptionConfirmation notificationType = message
- Header (Basic Access Authentication): The basic access authentication is a method for an HTTP user agent to provide a username and password when making a request. In basic HTTP authentication, a request contains a header field in the form of Authorization: Basic <credentials>, where <credentials> is the Base64 encoding of username and password joined by a single colon(: ).
-
Events:
Select the events that will trigger this webhook.- All Events:Receive a notification for every single event type.
- Select Events: Choose specific events to subscribe to (e.g., customer.business.create).
Step 3. Click "Save Webhook" to create the webhook.
Note:
By default, your new webhook will be created with a disabled status. It will not receive event notifications until you confirm the subscription.
2.2 Confirming your webhook subscription
Immediately after you save the webhook, the system sends a SubscriptionConfirmation message to the URL you configured. Your endpoint must process this message correctly to activate the subscription. The confirmation process depends on the authentication type you selected.
Important: A webhook can only be enabled and receive event payloads after the subscription has been successfully confirmed.
Method I: If You Selected "Header" Authentication
If you choose header-based authentication, your endpoint must perform a confirmation process.
- Step 1: Receive the Subscription Confirmation Payload
Your endpoint will receive a POST request with a JSON body similar to this:
"Type": "SubscriptionConfirmation",
"MessageId": "e7caa54f-7380-42ff-bf41-1a847b5db5a9",
"Token": "2336412f37fb687ff41a9668e",
"TopicArn": "arn:aws:sns:us-east-1:9853074181:test-topic1",
"Message": "You have chosen to subscribe to the topic arn:aws:sns:us-east-1:985323074181:test-topic1.\nTo confirm the subscription, visit the SubscribeURL included in this message.",
"SubscribeURL": "https://sns.us-east-1.amazonaws.com/?Action=ConfirmSubscription&TopicArn=arn:aws:sns:us-east-1:985323074181:test-topic1&Token=2336440d7df41a9668e",
"Timestamp": "2024-05-15T08:53:04.873Z",
"SignatureVersion": "1",
"Signature": "KwtRZ0gNGtkBIeMMzUkPVh/Ail3173vqyffdpebj1qHe1xVx0EOIM3tx3w==",
"SigningCertURL": "https://sns.us-east-1.amazonaws.com/SimpleNotificationService-60eadc530605d63b8e62a523676ef735.pem"
- Step 2: Respond to the Authentication Challenge:
Before confirming, your endpoint must first respond to the incoming request with an HTTP 401 status code and include the WWW-Authenticate = Basic response header. This proves to the system that your endpoint is expecting basic authentication.
Here's how it works:-
Server Challenge :
When a client attempts to access a protected resource without authentication, the server responds with a 401 Unauthorized HTTP status code. It includes a WWW-Authenticate: Basic header, indicating that Basic authentication is required. -
Client Response :
The client then combines the username and password with a colon (e.g., username: password) and encodes this string using Base64. -
Authorization Header :
The Base64-encoded string is then included in the Authorization header of subsequent requests, prefixed with "Basic " -
Server Verification:
The server receives the request, decodes the Base64 string to retrieve the plain-text username and password, and verifies these credentials against its stored user information. If the credentials are valid, the request is processed; otherwise, another 401 Unauthorized response is returned.
-
- Step 3: Confirm the Subscription:
After handling the authentication challenge, extract the SubscribeURL value from the JSON payload. Make a simple HTTP GET request to this URL. A successful request will confirm your subscription.
Sample code for confirming the webhook subscription:
This code checks if an incoming message is a subscription confirmation. If it is, the code finds the unique confirmation URL (SubscribeURL) inside the message body and prints it to the consol.
if (messageType === 'SubscriptionConfirmation') {
const confirmUrl = req.body.SubscribeURL;
console.log(`Confirming subscription at URL: ${confirmUrl}`);
https.get(confirmUrl, (res) => {
console.log(`Subscription confirmation status code: ${res.statusCode}`);
res.on('data', (chunk) => {
console.log(`Response chunk: ${chunk}`);
});
res.on('end', () => {
console.log('Subscription confirmation completed.');
});
}).on('error', (err) => {
console.error('Error confirming subscription:', err.message);
});
}- Step 4: Enable the Webhook:
Once the subscription is confirmed, you can enable the webhook in the portal to begin receiving live event notifications.
Note: The system will only send the webhook payload to the configured endpoint if:
- The SubscribeURL is confirmed, and
- The webhook is enabled.
Method II: If You Selected "Signature Validation" (Recommended)
This method is simpler to confirm but requires cryptographic validation for all subsequent notifications.
- Step 1: Receive the Subscription Confirmation Payload:
Your endpoint will receive a POST request with a JSON body.
{
"Type": "SubscriptionConfirmation",
"MessageId": "f0e2b66c-811c-42d8-8ec3-be2fbee937c6",
"Token": "2336412f37fb687f5d51e6e2425a8a587855b30a8726c02e5690a74304a897cc1a3ed03cb90e709bf596720bcc1dcaec2e0e208e2a0d0121c7d113c5f5217dc1cae64435b2ec3657efc6ee0c094b9b5fa6e6b62eabd9995cbb6935dd2f06dbeebd495c9f1642cad290e84c9d22d8ef2c3983326d7417181a76c29a0d9eb496b4",
"TopicArn": "arn:aws:sns:us-east-1:985323074181:passport_webhook_382",
"Message": "You have chosen to subscribe to the topic arn:aws:sns:us-east-1:985323074181:passport_webhook_382.\nTo confirm the subscription, visit the SubscribeURL included in this message.",
"SubscribeURL": "https://sns.us-east-1.amazonaws.com/?Action=ConfirmSubscription&TopicArn=arn:aws:sns:us-east-1:985323074181:passport_webhook_382&Token=2336412f37fb687f5d51e6e2425a8a587855b30a8726c02e5690a74304a897cc1a3ed03cb90e709bf596720bcc1dcaec2e0e208e2a0d0121c7d113c5f5217dc1cae64435b2ec3657efc6ee0c094b9b5fa6e6b62eabd9995cbb6935dd2f06dbeebd495c9f1642cad290e84c9d22d8ef2c3983326d7417181a76c29a0d9eb496b4",
"Timestamp": "2025-08-01T05:53:13.920Z",
"SignatureVersion": "2",
"Signature": "gPr1ak5NnOvzfK9bAOhsiHpH+DEQJqn8PvgBj8JDOZraYXpQhpiEVVGWeYnU/9cOdhSs/wsxg/nRqwy/Lrg2dgX0HFdhp2LQZnX11YrVJNd17kiJ6IwMkXPwQ2smwQ1LVfXDkAkSFwHdJCaoisPu4FV5de4qmSG11hw8W2tYyLPYcFF2Rh8uh76ampKIxwXHE8V+EkZXYmk9AoZNcAlN94aOOXixEkW6Mt4gpz0EIUVIPjBR5P8JC6NLFHHwDkMvzJR1uv5q7hLX5spNvvPFWWAJUAdf+r6KyrQUJQxsE7Gr8VOPBur2lSWEfB/6dKMKmqo7EiuQt10cIF5A2MrDpQ==",
"SigningCertURL": "https://sns.us-east-1.amazonaws.com/SimpleNotificationService-9c6465fa7f48f5cacd23014631ec1136.pem"
}- Step 2: Confirm the Subscription:
To activate, simply extract the SubscribeURL value from the JSON payload and make a HTTP GET request to it.
This code checks if an incoming message is a subscription confirmation. If it is, the code finds the unique confirmation URL (SubscribeURL) inside the message body and prints it to the console.
if (messageType = 'SubscriptionConfirmation') {
const confirmUrl = req.body.SubscribeURL;
console.log(`Confirming subscription at URL: ${confirmUrl
}`);
https.get(confirmUrl, (res) => {
console.log(`Subscription confirmation status code: ${res.statusCode
}`);
res.on('data', (chunk) => {
console.log(`Response chunk: ${chunk
}`);
});
res.on('end', () => {
console.log('Subscription confirmation completed.');
});
}).on('error', (err) => {
console.error('Error confirming subscription:', err.message);
});
}- Step 3: Enable the Webhook: Once the subscription is confirmed via the GET request, you can enable the webhook in the portal.
2.3 Handling Live Event Notifications
Once your webhook is confirmed and enabled, your endpoint will start receiving POST requests for the events you subscribed to.
2.3.1. If Using Header Authentication
In case of Header Authentication, the payload will not be having the Authorization header by default, if the authorization token doesn't exist then configured webhook endpoint should throw 401 HTTP status code along with 'WWW-Authenticate' header in the response headers. Post this, system will retry the delivery of payload along with Authorization header.
Sample code for webhook notification: This code defines a secure API endpoint designed to receive messages from Amazon SNS.
// Endpoint to handle SNS messages
app.post('/sns-endpoint', authMiddleware, (req, res) => {
// validate header
const authHeader = req.headers['authorization'];
if (!authHeader) {
res.setHeader('WWW-Authenticate', 'Basic');
return res.status(401).send('Authentication required.');
}
// TODO: authorization token validation
// Handle SNS message
const messageType = req.headers['x-amz-sns-message-type'];
if (messageType === 'Notification') {
// Handle SNS notification
const message = req.body.Message;
console.log(`Received message: ${message}`);
// Add business logic for payload handling
}
res.status(200).send('Message received.');
});2.3.2. If Using Signature Validation Authentication
Signature validation is the process of confirming that a message your system receives is authentic and has not been altered in transit. Each message includes a digital signature created using the a private key and a link to a public certificate. Your system retrieves this certificate, reconstructs the exact message data in the format it was signed, and verifies the signature against the public key. If the verification succeeds, you can trust that the message genuinely came from Passport and has not been tampered with; if it fails, the message should be rejected. This prevents malicious actors from sending fake or altered messages to your application.
The below is the sample code for handling the SNS message with Signature Validation in NodeJS.
// Required built-in modules
const https = require('https'); // For making HTTPS requests (e.g., confirming subscriptions, downloading certs)
const crypto = require('crypto'); // For signature verification
const { URL } = require('url'); // For parsing and validating SigningCertURL
// Certificate cache to avoid downloading the same certificate multiple times
const certCache = new Map();
// Import the Express module
const express = require('express');
// Create an Express application
const app = express();
// Middleware to parse incoming JSON requests
app.use(express.json());
// Simple logging middleware
app.use((req, res, next) => {
console.log(`${req.method} ${req.url}`);
next(); // Continue to the next middleware or route
});
const PORT = process.env.PORT || 3300;
app.listen(PORT, () => {
console.log(`Server running on http://localhost:${PORT}`);
});
// Define a basic route
app.get('/', (req, res) => {
res.send('Hello, World!');
});
/**
* Express route handler for receiving SNS messages
* This endpoint should be configured as the SNS subscription endpoint.
*/
app.post('/sns-endpoint', async (req, res) => {
const message = req.body; // SNS sends the message as JSON in the request body
try {
// Ensure the message uses supported signature version
if (message.SignatureVersion !== '2') {
return res.status(400).send('Unsupported SignatureVersion');
}
// Verify message authenticity using signature and certificate
const isValid = await verifySnsSignature(message);
if (!isValid) {
return res.status(403).send('Signature verification failed');
}
// Handle different SNS message types
if (message.Type === 'Notification') {
console.log('Received notification:', message.Message);
// TODO: Handle business logic using the message content
} else if (message.Type === 'UnsubscribeConfirmation') {
console.log('Unsubscribe confirmed.');
} else if (message.Type === 'SubscriptionConfirmation') {
// When SNS sends a confirmation message, confirm the subscription by visiting the URL
const confirmUrl = message.SubscribeURL;
console.log(`Confirming subscription at URL: ${confirmUrl}`);
https.get(confirmUrl, (resp) => {
console.log(`Subscription confirmation status code: ${resp.statusCode}`);
resp.on('data', (chunk) => {
console.log(`Response chunk: ${chunk}`);
});
resp.on('end', () => {
console.log('Subscription confirmation completed.');
});
}).on('error', (err) => {
console.error('Error confirming subscription:', err.message);
});
}
res.send('OK'); // Respond success to SNS
} catch (err) {
console.error('Error verifying SNS message:', err);
res.status(500).send('Internal server error');
}
});
/**
* Validates the format and domain of the SigningCertURL to ensure it's from a trusted AWS endpoint.
* @param {string} certUrl - The URL provided in the SNS message to download the signing certificate.
*/
function validateCertUrl(certUrl) {
const parsed = new URL(certUrl);
if (parsed.protocol !== 'https:') {
throw new Error('SigningCertURL must use HTTPS');
}
const host = parsed.hostname.toLowerCase();
if (!host.startsWith('sns.') || !host.endsWith('.amazonaws.com')) {
throw new Error('Invalid AWS SigningCertURL: ' + host);
}
}
/**
* Builds the canonical string that was signed by AWS.
* The structure of the string depends on the SNS message type.
* @param {object} message - The parsed SNS message object.
* @returns {string} The canonical string used to verify the signature.
*/
function buildStringToSign(message) {
const type = message.Type;
const lines = [];
if (type === 'Notification') {
lines.push('Message');
lines.push(message.Message);
lines.push('MessageId');
lines.push(message.MessageId);
if (message.Subject) {
lines.push('Subject');
lines.push(message.Subject);
}
lines.push('Timestamp');
lines.push(message.Timestamp);
lines.push('TopicArn');
lines.push(message.TopicArn);
lines.push('Type');
lines.push(message.Type);
} else if (type === 'SubscriptionConfirmation' || type === 'UnsubscribeConfirmation') {
lines.push('Message');
lines.push(message.Message);
lines.push('MessageId');
lines.push(message.MessageId);
lines.push('SubscribeURL');
lines.push(message.SubscribeURL);
lines.push('Timestamp');
lines.push(message.Timestamp);
lines.push('Token');
lines.push(message.Token);
lines.push('TopicArn');
lines.push(message.TopicArn);
lines.push('Type');
lines.push(message.Type);
} else {
throw new Error('Unknown message type');
}
// Each field must be followed by a newline as per AWS spec
return lines.map(line => `${line}\n`).join('');
}
/**
* Downloads the signing certificate from AWS and caches it.
* @param {string} certUrl - The URL from which to fetch the signing certificate.
* @returns {Promise<string>} - The PEM-encoded certificate as a string.
*/
function getCert(certUrl) {
if (certCache.has(certUrl)) {
return Promise.resolve(certCache.get(certUrl));
}
return new Promise((resolve, reject) => {
https.get(certUrl, (res) => {
if (res.statusCode !== 200) return reject(new Error('Cert download failed'));
let data = '';
res.on('data', (chunk) => (data += chunk));
res.on('end', () => {
certCache.set(certUrl, data); // Store cert for reuse
resolve(data);
});
}).on('error', reject);
});
}
/**
* Verifies the signature of the SNS message using AWS's public certificate.
* @param {object} message - The SNS message object.
* @returns {Promise<boolean>} True if the signature is valid, false otherwise.
*/
async function verifySnsSignature(message) {
// Validate URL and ensure it's from a trusted AWS domain
console.log('Verifying SNS Signature...');
validateCertUrl(message.SigningCertURL);
console.log('Cert URL:', message.SigningCertURL);
// Fetch the cert (cached if already retrieved)
const cert = await getCert(message.SigningCertURL);
// Build the string that was originally signed
const stringToSign = buildStringToSign(message);
console.log('---- Canonical StringToSign ----');
console.log(stringToSign);
console.log('--------------------------------');
// Decode the base64 signature
const signature = Buffer.from(message.Signature, 'base64');
console.log('Message Type:', message.Type);
console.log('Signature:', message.Signature);
// Verify using RSA SHA-256 algorithm
const verifier = crypto.createVerify('RSA-SHA256');
verifier.update(stringToSign, 'utf8');
verifier.end();
try {
const result = verifier.verify(cert, signature);
console.log('Signature valid:', result);
return result;
} catch (err) {
console.error('Signature verification error:', err);
return false;
}
}This Signature Validation method provides superior security. The body of each incoming request is a JSON object from the notification service. The actual event data is a stringified JSON object located in the "message" field and must be parsed (e.g., JSON.parse(body.message)) to be used.
2.4 Verification Process
2.4.1. If Using Header Authentication
Header-based authentication is a simpler but less secure verification method. It relies on validating a static, pre-shared secret sent in the HTTP Authorization header of each webhook request. Unlike signature validation, it does not provide cryptographic proof of message authenticity or integrity.
The verification process involves the following steps:
-
Header Extraction
Your endpoint must inspect the headers of every incoming webhook request and extract the value of the Authorization header. -
Token Validation
Compare the extracted header value against the secret token value you configured during the webhook setup. The comparison must be an exact match. -
Payload Processing
If the Authorization header value matches your pre-configured secret, the request is considered valid. You can then directly parse and process the JSON payload in the request body. No further verification steps are needed.
If the Authorization header is missing or its value does not match the expected secret, the request should be rejected, typically with a 401 Unauthorized or 403 Forbidden response.
// Sample Payload
{
"id": 15140,
"eventType": "customer.business.create",
"eventTimeStamp": "05/13/2025 01:03:00",
"eventId": "0001709618091430001",
"eventCreated": 1747098180090,
"payload": [
{
"resourceName": "customer",
"url": "/v1/customer/id/4007317",
"id": 4007317,
"externalId": "PMAcceptance-Mar26",
"type": "BUSINESS",
"business": {
"legalName": "AMEX",
"ein": "22-2015690",
"doingBusinessAs": "American Express",
"phone": "213-233-1731",
"email": "[email protected]",
"website": "www.amex.com",
"mailingAddress": [
{
"resourceName": "address",
"url": "/v1/customer/id/4007317/mailingAddress/id/1021541",
"id": 1021541,
"externalId": "PA09090991",
"addressLine1": "Ap 4891",
"addressLine2": "Conference Centre Ste 1020",
"city": "Sugar Notch",
"state": "PA",
"zip": "18706",
"isPrimary": true,
"usage": {
"isPayorAddress": false
}
}
],
"verification": {
"ofacStatus": "PENDING_VERIFICATION",
"ofacStatusReason": "PENDING_VERIFICATION",
"ofacStatusDate": "03/26/2023 06:46:41",
"cipStatus": "IGNORED",
"cipStatusReason": "IGNORED ON_USER_REQUEST",
"cipStatusDate": "03/26/2023 06:46:41"
},
"stateOfIncorporation": "CA",
"businessCategory": "LLC",
"dateOfIncorporation": "12/20/2000",
"beneficialOwner": [
{
"id": 7522,
"firstName": "Amelia",
"lastName": "Connor",
"fullName": "Amelia Connor",
"last4ssn": "2023",
"dob": "10/01/2000",
"homePhone": "530-986-1172",
"mobilePhone": "235-237-4107",
"workPhone": "838-900-9090",
"email": "[email protected]",
"mailingAddress": [
{
"id": 1021542,
"externalId": "PC30945678911",
"addressLine1": "Ap 4391",
"addressLine2": "Conference Centre Ste 1020",
"city": "Sugar Notch",
"state": "PA",
"zip": "18706",
"isPrimary": true
}
],
"verification": {
"ofacStatus": "PENDING_VERIFICATION",
"ofacStatusReason": "PENDING_VERIFICATION",
"ofacStatusDate": "03/26/2023 06:46:41",
"cipStatus": "PENDING_VERIFICATION",
"cipStatusReason": "PENDING_VERIFICATION",
"cipStatusDate": "03/26/2023 06:53:19"
},
"isUSCitizen": true,
"actAsAuthorizedSignatory": true,
"businessDetails": {
"ownershipPercentage": 50.0,
"title": "Secretary"
},
"secondaryIdentification": {
"lastFourId": "2905",
"id": "48832905",
"type": "DRIVER_LICENSE",
"stateOfIssuance": "CA"
},
"pullCreditReport": false
}
]
},
"isPaperless": true,
"status": "ACTIVE",
"statusReason": "ON_USER_REQUEST",
"statusDate": "03/26/2023 06:46:41",
"account": {
"resourceName": "account",
"url": "/v1/customer/id/4007317/account"
},
"externalAccount": {
"resourceName": "externalAccount",
"url": "/v1/customer/id/4007317/externalAccount"
},
"card": {
"resourceName": "card",
"url": "/v1/customer/id/4007317/card"
},
"merchant": {
"resourceName": "merchant",
"url": "/v1/customer/id/4007317/merchant"
},
"createdOn": "03/26/2023 06:46:41",
"createdBy": {
"userType": "API_USER",
"username": "[email protected]",
"status": "ACTIVE"
},
"lastUpdatedOn": "03/26/2023 06:46:41",
"lastUpdatedBy": {
"userType": "API_USER",
"username": "[email protected]",
"status": "ACTIVE"
}
}
]
}Navigate to the Webhook Event Types page, to know more about different webhook events and their payloads.
2.4.2 If Using Signature Validation Authentication
Each webhook payload includes a Signature, SignatureVersion, and SigningCertURL, along with a webhook message.
1. Signature Version Validation
Signature Version for the webhook subscription with signature validation should be 2 (SHA256). Any message using a different version is rejected with a 400 Bad Request response.
2. Certificate URL Validation
The certificate used to verify the message signature must be hosted by AWS. The API validates that the SigningCertURL:
- Uses the HTTPS protocol
- Belongs to a trusted AWS SNS domain
(e.g.,https://sns.us-east-1.amazonaws.com/...)
If the URL does not meet these conditions, the message is rejected.
3. Certificate Retrieval
If the certificate URL is valid and not previously cached, a configured webhook endpoint can download the X.509 certificate from the SigningCertURL and stores it in memory for reuse.
4. Canonical String Construction
The API constructs a canonical string representation of the message using specific fields in a prescribed order. The fields and order vary depending on the message Type, such as Notification, SubscriptionConfirmation.
Example for a Notification type:
Message
{"eventId":"..."}
MessageId
1234-5678
Timestamp
2025-08-05T12:00:00Z
TopicArn
arn:aws:sns:...
Type
Notification
//Example for a SubscriptionConfirmation type:
Message
You have chosen to subscribe to the topic arn:aws:sns:us-east-1:***:passport_webhook_.
To confirm the subscription, visit the SubscribeURL included in this message.
MessageId
f0e2b66c-811c-42d8-8ec3-be2fbee937c6
SubscribeURL
https://sns.us-east-1.amazonaws.com/?Action=ConfirmSubscription&...
Timestamp
2025-08-01T05:53:13.920Z
Token
2336412f37fb68...
TopicArn
arn:aws:sns:us-east-1:...
Type
SubscriptionConfirmation5. Signature Decoding and Verification
The base64-encoded Signature value from the SNS message is decoded and verified using the RSA-SHA256 algorithm. The verification is performed using the public key in the downloaded certificate.
If the signature is successfully verified against the canonical string, the message is considered authentic and can process from your end. Otherwise, the API should return a 403 Forbidden response.
FUNCTION VerifySignature(stringToSign, signature, cert):
1. Initialize a verifier with algorithm 'RSA-SHA256'
2. Feed the canonical string (UTF-8 encoded) to the verifier
3. Finalize the input
4. Verify the signature using the public certificate
5. Return true if valid, false otherwiseCaching: To optimize performance and reduce external calls, the certificate fetched from SigningCertURL is cached in memory for the duration of the application runtime. If a new or previously unseen certificate URL is encountered, Can fetch again and add to the cache.
Payload Processing: Below is a sample of the full JSON payload you would receive for a notification and to securely process this payload, you must:
-
Parse the outer JSON to access its fields.
-
Verify the Signature: Before trusting the data, cryptographically verify the message Signature. This process involves:
- Ensuring the SignatureVersion is 2.
- Validating that the SigningCertURL is a legitimate https://sns.*.amazonaws.com domain.
- Fetching the public certificate from the SigningCertURL(it's recommended to cache this certificate).
- Building the canonical "string-to-sign" by concatenating specific fields from the message in a precise order and format.
- Using the downloaded public certificate to verify that the signature matches the canonical string.
-
Extract the Event Data: If the signature is valid, parse the Message string (JSON.parse(body.Message)) to get the actual event payload, which contains the eventType, payload, and other details.
Common Integration Scenarios
Scenario 1: Set up real-time transaction monitoring for a payments platform
A platform wants to receive instant notifications whenever a transaction is created, updated, or completed, so it can update customer balances and trigger downstream workflows in real time.
Steps:
- Create a webhook subscription via POST
/v1/customer/id/{id}/webhookSubscriptionwith:url: Your HTTPS endpoint (e.g.,https://api.yourplatform.com/webhooks/treasury)subscribedEventType:["transaction.ach.create", "transaction.ach.update", "transaction.wire.create", "transaction.wire.update"]
- Verify the subscription is active using GET
/v1/customer/id/{id}/webhookSubscription/id/{id}. - When a transaction is created or its status changes, your endpoint receives a JSON payload with the full transaction resource.
- Your system validates the payload, updates the customer's dashboard, and triggers any downstream processing (e.g., ledger updates, notifications).
Scenario 2: Monitor card lifecycle events
A platform wants to push real-time card status updates to its mobile app, so customers see instant notifications when their card is activated, frozen, or cancelled.
Steps:
- Create a webhook subscription with:
subscribedEventType: card issuance lifecycle events (e.g., card create, card update)
- Enable the subscription.
- When a card status changes (e.g.,
PENDING→ACTIVE,ACTIVE→FREEZED), your endpoint receives the event. - Your mobile app pushes a notification to the customer: "Your card is now active and ready to use."
Scenario 3: Temporarily pause webhooks during a maintenance window
Your platform has a scheduled maintenance window and you need to stop receiving webhook events temporarily without losing your subscription configuration.
Steps:
- Disable the webhook via POST
/v1/customer/id/{id}/webhookSubscription/id/{id}/disable. - Events that occur during the maintenance window will not be delivered.
- After maintenance, re-enable the webhook via POST
/v1/customer/id/{id}/webhookSubscription/id/{id}/enable. - Use list or GET APIs to reconcile any events that occurred during the downtime.
Updated 13 days ago