Webhooks
Overview
Webhooks are at the centre of how Vantage API is able to keep you connected to the kit throughout the fulfilment process. This guide will outline how we use webhooks and how you can integrate with them.
Webhook Verification
Diagnostics API uses HMAC-SHA256 with your unique signing secret to generate webhook signatures
Below is some example code on how to verify a webhook secret
import hmac
import hashlib
import time
from flask import Flask, request, abort
app = Flask(__name__)
SIGNING_SECRET = "your_signing_secret_here"
TIMESTAMP_TOLERANCE = 300000 # 5 minutes in milliseconds
def verify_webhook(request):
# 1. Get signature header
signature_header = request.headers.get('X-Terra-Signature')
if not signature_header:
return False, "Missing signature header"
# 2. Parse signature header
parts = dict(item.split('=') for item in signature_header.split(','))
try:
timestamp = int(parts.get('t', 0))
received_signature = parts.get('v1', '')
except (ValueError, AttributeError):
return False, "Invalid signature format"
# 3. Verify timestamp
current_time = int(time.time() * 1000)
age = current_time - timestamp
if abs(age) > TIMESTAMP_TOLERANCE:
return False, "Timestamp too old or in future"
# 4. Get raw body
body = request.get_data(as_text=True)
# 5. Construct signed string
signed_string = f"{timestamp}.{body}"
# 6. Calculate expected signature
expected_signature = hmac.new(
SIGNING_SECRET.encode('utf-8'),
signed_string.encode('utf-8'),
hashlib.sha256
).hexdigest()
# 7. Compare signatures (constant-time)
if not hmac.compare_digest(expected_signature, received_signature):
return False, "Signature mismatch"
return True, None
@app.route('/webhooks/terra', methods=['POST'])
def webhook_handler():
valid, error = verify_webhook(request)
if not valid:
abort(401, description=error)
# Process webhook
data = request.get_json()
print(f"Received webhook: {data}")
return '', 200
if __name__ == '__main__':
app.run(port=3000)
Headers
Every webhook Terra sends include these headers
X-Terra-Signature
HMAC signature with timestamp
t=1700000000000,v1=a1b2c3...
X-Terra-Trace-Id
Unique request ID for debugging
251285377982321505
Content-Type
Always application/json
application/json
Signature Format
The X-Terra-Signature header follows this format:
t= Unix timestamp in milliseconds when the webhook was sentv1= HMAC-SHA256 signature in hexadecimal format
Example:
Body
fulfilment.payment_processing is not actually returned as a webhook but is part of the response once an order is placed
fulfilment.payment_processing
This is the default event returned once an order is placed. Not actually returned as an individual webhook.
Look at hint above
fulfilment.payment_complete
This event is sent once payment is confirmed
fulfilment.delivery_fulfilled
This event is sent once delivery details such as tracking number is available
results.kit_activated
The event is sent once an end user activates his/her kit. Only applies to suppliers which support 2-step activation process
results.sample_processing_in_lab
This event is sent when test receipt is confirmed by the lab
results.results_ready
This event is sent once result availability is confirmed by lab
results.sample_rejected
This event is sent if the sample is rejected by the lab
results.escalation_raised
This event is sent if there is an escalation in the result set
Last updated
Was this helpful?