Setup Your Webhook Integration
This comprehensive guide walks you through the complete process of setting up and using webhooks with Visiwise. You'll learn how to add webhook endpoints, test your integration, retrieve your webhook secret, and securely handle incoming webhook events in your application.
Add a Webhook Endpoint
Once configured, your endpoint will receive webhook events from Visiwise whenever the selected event types occur.
Step 1: Navigate to the Integrations Page
- Log in to your Visiwise dashboard
- Navigate to the API & Integrations page from the main menu
Step 2: Select Webhook Integration
- On the Integrations page, locate and select the Webhook integration option
- This will open the webhook integration configuration page
Step 3: Go to the Endpoints Tab
- In the webhook integration page, click on the Endpoints tab
- This tab displays all your configured webhook endpoints
Step 4: Add a New Endpoint
- Click the Add Endpoint button
- A form will appear for configuring your new webhook endpoint
Step 5: Configure Your Endpoint
Fill in the endpoint configuration:
-
Endpoint URL: Enter the full URL where you want to receive webhook events (e.g.,
https://your-app.com/webhooks/visiwise)- The URL must be publicly accessible (not localhost)
- The URL must use HTTPS
- The endpoint should be ready to receive POST requests
-
Description: An optional description for your own reference.
-
Event Types: Select the event types that should be sent to this URL
- You can select multiple event types
- Only selected event types will trigger webhooks to this endpoint
- You can modify this selection later
-
Enabled: Check this box to enable the endpoint, or uncheck it to disable it.
Step 6: Submit Your Configuration
- Review your endpoint configuration
- Click Create Endpoint to save the endpoint
- Your webhook endpoint is now active and will start receiving events for the selected event types
Test Your Webhook Integration
Testing your webhook integration ensures that events are being sent correctly and your endpoint is receiving and processing them properly. This section shows you how to test webhooks using the Visiwise dashboard.
Method 1: Trigger a Real Event
The most reliable way to test your webhook integration is to trigger a real event that will generate a webhook.
Step 1: Track a New Container
- In your Visiwise dashboard, navigate to the Track Shipment page from the main menu
- Track a new shipment using a container number, bill of lading, or booking number
- This action will trigger webhook events when shipment tracking finishes if your endpoint is configured to receive the relevant event types
Step 2: Verify the Webhook Event
- Go to the API & Integrations page in your dashboard
- Select the Webhook integration
- Click on the Endpoints tab
- Find your webhook endpoint and click on View messages
- You should see the webhook event that was sent to your endpoint
Step 3: Check Event Details
In the Messages tab, you can see information about your event such as:
- URL: The URL that the event was sent to
- Status: Whether the webhook was successfully delivered (e.g.,
Success,Fail,Pending) - Timestamp: When the event was sent
You can view more details about the event by clicking on the View button.
Method 2: Manual Retry
If a webhook event failed to deliver, you can manually resend it:
- Go to the Messages tab for your webhook endpoint
- Find the failed or pending event
- Click the Resend button
- The webhook will be sent again to your endpoint
If a webhook fails, check:
- Your endpoint URL is correct and accessible
- Your endpoint is returning a 2xx HTTP status code
- Your endpoint is responding within the timeout period
- Your server logs for any errors
Verifying Your Endpoint
To ensure your endpoint is working correctly:
- Check HTTP Status Codes: Your endpoint should return a
200 OKor202 Acceptedstatus code to acknowledge receipt - Response Time: Your endpoint should respond quickly (ideally within a few seconds)
- Logging: Check your application logs to confirm events are being received
- Payload Validation: Verify that the event payload matches the expected schema
Common Issues
Webhook Not Received
If you don't see webhook events in the Messages tab:
- Verify the event type is selected for your endpoint
- Check that the event actually occurred (e.g., container was tracked)
- Ensure your endpoint URL is correct and publicly accessible
Webhook Delivery Failed
If webhooks show as failed:
- Verify your endpoint is online and accessible
- Check that your endpoint accepts POST requests
- Ensure your endpoint returns a valid HTTP response
- Review your server error logs
- Check for firewall or network issues blocking requests
Delayed Events
Webhook events are typically sent within seconds of the triggering event. If events are delayed:
- Check the Messages tab for the actual send timestamp
- Verify your endpoint response time isn't causing delays
- Contact support if delays persist
Best Practices for Testing
- Test in Development First: Use a development or staging environment to test webhooks before deploying to production
- Monitor the Messages Tab: Regularly check the Messages tab to ensure webhooks are being delivered successfully
- Test Different Event Types: Verify that all selected event types trigger webhooks correctly
- Validate Payloads: Ensure your application correctly parses and handles different event payloads
Retrieve Your Webhook Secret
Your webhook secret is a critical security credential used to verify that incoming webhook events are authentic and come from Visiwise. This section shows you how to retrieve your webhook secret from the dashboard.
Your webhook secret is like a password—treat it with the same level of security. Never expose it in client-side code, commit it to version control, or share it publicly. Store it securely in environment variables or a secrets management system.
Step 1: Navigate to Webhook Endpoints
- Log in to your Visiwise dashboard
- Go to the API & Integrations page
- Select the Webhook integration
- Click on the Endpoints tab
Step 2: Locate Your Endpoint
- Find the webhook endpoint for which you want to retrieve the secret
Step 3: Access the Secret
- In your endpoint's row, locate the Secret button
- Click the Secret button
- The webhook secret will be displayed (it is masked initially)
Step 4: Copy and Store the Secret
- Click on the Copy button to copy the secret to your clipboard
- Store it securely in one of the following ways:
- Environment variables (e.g.,
VISIWISE_WEBHOOK_SECRET) in your application - Secrets management system (e.g., AWS Secrets Manager, HashiCorp Vault)
- Secure configuration files (never commit to version control)
- CI/CD pipeline secrets
- Environment variables (e.g.,
- Rotate secrets periodically for enhanced security
- Never log or print the secret in your application logs
Using the Secret
Once you have your webhook secret, you'll use it to validate incoming webhook events. The secret is used to verify the webhook signature, ensuring that events are authentic and haven't been tampered with.
For detailed instructions on how to use the secret to validate webhook events, see the Handle Webhook Events section below.
Secret Rotation
If your secret is ever compromised or you need to rotate it for security reasons:
- Go to the Endpoints tab in the webhook integration page
- Find your webhook endpoint
- Click the Secret button
- Click the Rotate Secret button
- A new secret will be generated and the old secret will be immediately invalidated
- Update your application with the new secret
After rotating a secret, the old secret will stop working immediately. Make sure to update your application configuration before rotating, or coordinate the rotation to minimize downtime.
Handle Webhook Events
This section explains how to securely receive, validate, and process webhook events from Visiwise in your application. Proper handling ensures that you only process authentic events and maintain the security of your integration.
Webhook Event Structure
Visiwise webhooks send events as HTTP POST requests with the following characteristics:
- Content-Type:
application/json - Headers: Include Svix headers (
svix-id,svix-timestamp,svix-signature) for validation - Body: JSON payload containing the event data
Step 1: Validate Webhook Event Timestamp
The first security check is to validate that the event timestamp is recent, preventing replay attacks.
Why Validate Timestamp?
Events with old timestamps may be replay attacks. By checking that the timestamp is within an acceptable window (e.g., 5 minutes), you can reject stale events.
Implementation
- Python
- Node.js
import time
def validate_timestamp(timestamp, tolerance=300):
"""
Validate timestamp to prevent replay attacks.
Args:
timestamp: Timestamp string to validate
tolerance: Time tolerance in seconds (default: 5 minutes)
Returns:
bool: True if timestamp is within tolerance
"""
if not timestamp:
return False
try:
current_time = int(time.time())
timestamp_int = int(timestamp)
time_diff = abs(current_time - timestamp_int)
if time_diff > tolerance:
return False
return True
except (ValueError, TypeError):
return False
function validateTimestamp(timestamp, tolerance = 300) {
/**
* Validate timestamp to prevent replay attacks.
*
* @param {string} timestamp - Timestamp string to validate
* @param {number} tolerance - Time tolerance in seconds (default: 5 minutes)
* @returns {boolean} True if timestamp is within tolerance
*/
if (!timestamp) {
return false;
}
try {
const currentTime = Math.floor(Date.now() / 1000);
const timestampInt = parseInt(timestamp, 10);
const timeDiff = Math.abs(currentTime - timestampInt);
return timeDiff <= tolerance;
} catch (error) {
return false;
}
}
Step 2: Validate Webhook Signature
The most critical security step is validating the webhook signature using your secret. This ensures the event is authentic and hasn't been tampered with.
How Signature Validation Works
Visiwise uses Svix-style signature verification with HMAC-SHA256. The signature is computed from a combination of the webhook ID, timestamp, and payload. You'll receive these values in Svix headers:
svix-id: Unique identifier for the webhook eventsvix-timestamp: Unix timestamp of when the event was createdsvix-signature: The computed signature(s)
The signature header may contain multiple signatures (space-separated) with version prefixes (e.g., v1,signature). You should validate against all provided signatures.
Implementation
- Python
- Node.js
import base64
import hmac
import hashlib
from flask import Request
def validate_svix_signature(request: Request, payload: bytes, secret: str) -> bool:
"""
Validate webhook signature using Svix-style verification.
Args:
request: Flask request object
payload: Raw request payload
secret: Webhook secret key (with or without 'whsec_' prefix)
Returns:
bool: True if signature is valid
"""
if not secret:
return False
try:
# Get required headers
svix_id = request.headers.get("svix-id")
svix_timestamp = request.headers.get("svix-timestamp")
svix_signature = request.headers.get("svix-signature")
if not all([svix_id, svix_timestamp, svix_signature]):
return False
# Remove 'whsec_' prefix if present
if secret.startswith("whsec_"):
secret = secret[6:]
# Decode the secret from base64
secret_bytes = base64.b64decode(secret)
# Construct the signed content
signed_content = f"{svix_id}.{svix_timestamp}.{payload.decode('utf-8')}"
# Compute the expected signature
expected_signature = hmac.new(
secret_bytes, signed_content.encode("utf-8"), hashlib.sha256
).digest()
expected_signature_b64 = base64.b64encode(expected_signature).decode()
# Parse the signature header (format: "v1,signature v1,other_signature")
signatures = svix_signature.split(" ")
for sig in signatures:
signature = sig.split(",")[1] # Remove 'v1,' prefix
if hmac.compare_digest(signature, expected_signature_b64):
return True
return False
except Exception:
return False
const crypto = require("crypto");
function validateSvixSignature(req, payload, secret) {
/**
* Validate webhook signature using Svix-style verification.
*
* @param {object} req - Express request object
* @param {Buffer|string} payload - Raw request payload
* @param {string} secret - Webhook secret key (with or without 'whsec_' prefix)
* @returns {boolean} True if signature is valid
*/
if (!secret) {
return false;
}
try {
// Get required headers
const svixId = req.headers["svix-id"];
const svixTimestamp = req.headers["svix-timestamp"];
const svixSignature = req.headers["svix-signature"];
if (!svixId || !svixTimestamp || !svixSignature) {
return false;
}
// Remove 'whsec_' prefix if present
let secretKey = secret;
if (secretKey.startsWith("whsec_")) {
secretKey = secretKey.substring(6);
}
// Decode the secret from base64
const secretBytes = Buffer.from(secretKey, "base64");
// Construct the signed content
const payloadString =
typeof payload === "string" ? payload : payload.toString("utf-8");
const signedContent = `${svixId}.${svixTimestamp}.${payloadString}`;
// Compute the expected signature
const expectedSignature = crypto
.createHmac("sha256", secretBytes)
.update(signedContent)
.digest("base64");
// Parse the signature header (format: "v1,signature v1,other_signature")
const signatures = svixSignature.split(" ");
for (const sig of signatures) {
const signature = sig.split(",")[1]; // Remove 'v1,' prefix
if (
crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expectedSignature)
)
) {
return true;
}
}
return false;
} catch (error) {
return false;
}
}
Step 3: Acknowledge the Webhook
After validating the webhook, you should acknowledge it as quickly as possible by returning a successful HTTP response.
Why Acknowledge Quickly?
- Prevents Retries: Visiwise may retry failed webhooks. A quick acknowledgment prevents unnecessary retries.
- Better Performance: Your endpoint can return immediately and process the event asynchronously if needed.
- Reliability: Quick responses reduce the chance of timeouts.
Implementation Pattern
The recommended pattern is to:
- Validate timestamp and signature (synchronous, fast)
- Return a 200 OK response immediately
- Process the event asynchronously (in a background job, queue, etc.)
Complete Example
Here's a complete example that combines all three steps:
- Python
- Node.js
from flask import Flask, request, jsonify
import os
import threading
app = Flask(__name__)
def process_event(event):
"""Process webhook event."""
event_type = event.get('type')
event_data = event.get('data')
print(f"Received {event_type} event")
# Your event handling logic here
# ...
@app.route('/webhooks/visiwise', methods=['POST'])
def handle_webhook():
try:
# Get raw payload for signature validation (must be done before parsing JSON)
payload = request.get_data()
# Parse JSON from raw payload
import json
event = json.loads(payload.decode('utf-8'))
# 1. Validate Timestamp
svix_timestamp = request.headers.get("svix-timestamp")
if not validate_timestamp(svix_timestamp):
return jsonify({"error": "Invalid timestamp"}), 401
# 2. Validate signature
secret = os.environ.get('VISIWISE_WEBHOOK_SECRET')
if not validate_svix_signature(request, payload, secret):
return jsonify({"error": "Invalid signature"}), 401
# 3. Acknowledge immediately
# Process event asynchronously
threading.Thread(target=process_event, args=(event,)).start()
return jsonify({"status": "received"}), 200
except Exception as e:
print(f"Error processing webhook: {e}")
return jsonify({"error": "Internal server error"}), 500
const express = require("express");
const app = express();
function processEvent(event) {
console.log(`Received ${event.type} event`);
// Your event handling logic here
// ...
}
app.post(
"/webhooks/visiwise",
express.raw({ type: "application/json" }),
(req, res) => {
try {
// Get raw payload as string for both parsing and signature validation
const payloadString = req.body.toString();
const event = JSON.parse(payloadString);
// 1. Validate Timestamp
const svixTimestamp = req.headers["svix-timestamp"];
if (!validateTimestamp(svixTimestamp)) {
return res.status(401).json({ error: "Invalid timestamp" });
}
// 2. Validate signature
const secret = process.env.VISIWISE_WEBHOOK_SECRET;
if (!validateSvixSignature(req, payloadString, secret)) {
return res.status(401).json({ error: "Invalid signature" });
}
// 3. Acknowledge immediately
res.json({ status: "received" });
// Process event asynchronously
setImmediate(() => processEvent(event));
} catch (error) {
console.error("Error processing webhook:", error);
res.status(500).json({ error: "Internal server error" });
}
}
);
Security Checklist
When handling webhook events, ensure you:
- ✅ Validate the event timestamp to prevent replay attacks
- ✅ Validate the webhook signature using your secret
- ✅ Return a 200 OK response quickly to acknowledge receipt
- ✅ Process events asynchronously when possible
- ✅ Log webhook events for debugging and auditing
- ✅ Handle errors gracefully and return appropriate HTTP status codes
- ✅ Never expose your webhook secret in logs or error messages
Next Steps
Now that you can securely handle webhook events, you can:
- Implement event-specific processing logic for different event types
- Set up monitoring and alerting for failed webhook processing
- Review the Webhook Event Types API Reference for detailed event schemas