Webhook Events Reference
Complete reference for all 14 TitanCard webhook events with payload examples
TitanCard webhooks deliver real-time HTTP POST notifications when events occur in your account. This reference documents all available events, their payload structures, and how to verify webhook signatures.
Webhook Payload Structure
Every webhook delivers a JSON payload with this structure:
{
"event": "connection.created",
"timestamp": 1705420800000,
"data": {
// Event-specific data
}
}HTTP Headers
Each webhook request includes these headers:
Content-Type: application/json
X-Webhook-Signature: sha256=abc123...
X-Webhook-Event: connection.created
User-Agent: TitanCard-Webhooks/1.0Signature Verification
Always verify the X-Webhook-Signature header to ensure webhooks are authentic. The signature is an HMAC-SHA256 hash of the request body using your webhook secret.
Node.js / JavaScript
const crypto = require('crypto');
function verifyWebhookSignature(payload, signature, secret) {
const expectedSignature = 'sha256=' +
crypto.createHmac('sha256', secret)
.update(payload)
.digest('hex');
// Use timing-safe comparison to prevent timing attacks
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expectedSignature)
);
}
// Express.js example
app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
const signature = req.headers['x-webhook-signature'];
const isValid = verifyWebhookSignature(req.body, signature, process.env.WEBHOOK_SECRET);
if (!isValid) {
return res.status(401).json({ error: 'Invalid signature' });
}
const event = JSON.parse(req.body);
// Handle the event...
res.status(200).json({ received: true });
});Python
import hmac
import hashlib
def verify_webhook_signature(payload: bytes, signature: str, secret: str) -> bool:
expected = 'sha256=' + hmac.new(
secret.encode(),
payload,
hashlib.sha256
).hexdigest()
return hmac.compare_digest(signature, expected)
# Flask example
@app.route('/webhook', methods=['POST'])
def webhook():
signature = request.headers.get('X-Webhook-Signature')
if not verify_webhook_signature(request.data, signature, WEBHOOK_SECRET):
return {'error': 'Invalid signature'}, 401
event = request.json
# Handle the event...
return {'received': True}, 200Card Events
card.created
Triggered when a new business card is created.
{
"event": "card.created",
"timestamp": 1705420800000,
"data": {
"card_id": "k57abc123def",
"name": "John Smith",
"slug": "john-smith",
"title": "Software Engineer",
"company": "Acme Inc",
"email": "john@acme.com",
"card_type": "professional",
"created_at": 1705420800000
}
}card.updated
Triggered when card details are modified.
{
"event": "card.updated",
"timestamp": 1705420800000,
"data": {
"card_id": "k57abc123def",
"name": "John Smith",
"slug": "john-smith",
"title": "Senior Software Engineer",
"company": "Acme Inc",
"changed_fields": ["title"],
"updated_at": 1705420800000
}
}card.deleted
Triggered when a card is deleted.
{
"event": "card.deleted",
"timestamp": 1705420800000,
"data": {
"card_id": "k57abc123def",
"slug": "john-smith",
"deleted_at": 1705420800000
}
}card.viewed
Triggered when someone views your card profile.
{
"event": "card.viewed",
"timestamp": 1705420800000,
"data": {
"card_id": "k57abc123def",
"card_slug": "john-smith",
"view_source": "qr",
"viewer_country": "United States",
"viewer_city": "San Francisco",
"total_views": 142,
"viewed_at": 1705420800000
}
}card.shared
Triggered when your card is shared via QR code, NFC tap, or link copy.
{
"event": "card.shared",
"timestamp": 1705420800000,
"data": {
"card_id": "k57abc123def",
"card_name": "John Smith",
"card_slug": "john-smith",
"share_source": "nfc",
"total_views": 142,
"total_shares": 28,
"viewer_country": "United States",
"viewer_city": "San Francisco",
"shared_at": 1705420800000
}
}Connection Events
connection.created
Triggered when you make a new connection (most common event for CRM sync).
{
"event": "connection.created",
"timestamp": 1705420800000,
"data": {
"id": "j57xyz789ghi",
"first_name": "Jane",
"last_name": "Doe",
"full_name": "Jane Doe",
"email": "jane@example.com",
"phone": "+1-555-123-4567",
"company": "Tech Corp",
"title": "Product Manager",
"website": "https://techcorp.com",
"linkedin": "https://linkedin.com/in/janedoe",
"twitter": "@janedoe",
"address": "San Francisco, CA",
"notes": "Met at TechCrunch Disrupt",
"tags": ["conference", "product"],
"source": "qr_scan",
"direction": "inbound",
"status": "new",
"message": "Great meeting you!",
"meeting_event": "TechCrunch Disrupt 2026",
"meeting_location": "San Francisco, CA",
"meeting_date": 1705420800000,
"meeting_method": "in_person",
"created_at": 1705420800000,
"updated_at": null,
"last_contacted_at": null
}
}connection.updated
Triggered when connection details are modified.
{
"event": "connection.updated",
"timestamp": 1705420800000,
"data": {
"id": "j57xyz789ghi",
"first_name": "Jane",
"last_name": "Doe",
"full_name": "Jane Doe",
"email": "jane@example.com",
"phone": "+1-555-123-4567",
"company": "Tech Corp",
"title": "Senior Product Manager",
"notes": "Promoted in Q1 2026",
"tags": ["conference", "product", "follow-up"],
"status": "contacted",
"updated_at": 1705420800000,
"last_contacted_at": 1705420800000
}
}connection.deleted
Triggered when a connection is archived or permanently deleted.
{
"event": "connection.deleted",
"timestamp": 1705420800000,
"data": {
"id": "j57xyz789ghi",
"email": "jane@example.com",
"full_name": "Jane Doe",
"deleted_at": 1705420800000
}
}connection.tagged
Triggered when tags are added to or removed from a connection.
{
"event": "connection.tagged",
"timestamp": 1705420800000,
"data": {
"id": "j57xyz789ghi",
"full_name": "Jane Doe",
"email": "jane@example.com",
"tags": ["vip", "conference", "product"],
"tags_added": ["vip"],
"tags_removed": [],
"tagged_at": 1705420800000
}
}connection.status_changed
Triggered when a connection's pipeline status changes (e.g., new → contacted → qualified).
{
"event": "connection.status_changed",
"timestamp": 1705420800000,
"data": {
"id": "j57xyz789ghi",
"full_name": "Jane Doe",
"email": "jane@example.com",
"company": "Tech Corp",
"previous_status": "contacted",
"new_status": "qualified",
"changed_at": 1705420800000
}
}Reminder Events
reminder.created
Triggered when a follow-up reminder is scheduled for a connection.
{
"event": "reminder.created",
"timestamp": 1705420800000,
"data": {
"id": "r89reminder123",
"connection_id": "j57xyz789ghi",
"scheduled_for": 1705507200000,
"message": "Follow up about partnership opportunity",
"status": "pending",
"type": "follow_up",
"contact_name": "Jane Doe",
"contact_email": "jane@example.com",
"contact_company": "Tech Corp"
}
}reminder.due
Triggered when a scheduled reminder becomes due.
{
"event": "reminder.due",
"timestamp": 1705507200000,
"data": {
"id": "r89reminder123",
"connection_id": "j57xyz789ghi",
"scheduled_for": 1705507200000,
"message": "Follow up about partnership opportunity",
"status": "due",
"type": "follow_up",
"contact_name": "Jane Doe",
"contact_email": "jane@example.com",
"contact_company": "Tech Corp"
}
}Other Events
note.added
Triggered when a note is added to a connection.
{
"event": "note.added",
"timestamp": 1705420800000,
"data": {
"id": "n12note456",
"connection_id": "j57xyz789ghi",
"note_content": "Had a great call discussing Q2 plans. She's interested in our enterprise offering.",
"contact_name": "Jane Doe",
"contact_email": "jane@example.com",
"added_at": 1705420800000
}
}enrichment.completed
Triggered when contact data enrichment completes for a connection.
{
"event": "enrichment.completed",
"timestamp": 1705420800000,
"data": {
"connection_id": "j57xyz789ghi",
"enriched_fields": ["linkedin", "company", "title", "location"],
"status": "success",
"provider": "clearbit",
"completed_at": 1705420800000
}
}Field Reference
Common fields across connection events and their data types:
Field Type Description
──────────────────────────────────────────────────────────────────────
id string Unique connection identifier
first_name string Contact's first name
last_name string Contact's last name
full_name string Computed: first_name + last_name
email string Primary email address
phone string Phone number with country code
company string Company or organization name
title string Job title
website string Company or personal website URL
linkedin string LinkedIn profile URL
twitter string Twitter/X handle or URL
address string City, state, or full address
notes string User notes about the connection
tags array Array of tag strings
source string How connected: qr_scan, nfc, manual, import
direction string inbound (they found you) or outbound
status string Pipeline status: new, contacted, qualified, etc.
message string Initial message from connection
meeting_event string Event name where you met
meeting_location string Location where you met
meeting_date number Unix timestamp of meeting
meeting_method string in_person, virtual, phone, email
created_at number Unix timestamp when created
updated_at number Unix timestamp when last updated
last_contacted_at number Unix timestamp of last contactError Handling
Your webhook endpoint should:
- Return a 2xx status code within 30 seconds
- Handle duplicate events idempotently (use the event timestamp)
- Return 401 if signature verification fails
- Return 400 for malformed payloads
Retry Behavior
If your endpoint doesn't respond with 2xx, TitanCard will:
- Retry up to 3 times with exponential backoff (1min, 5min, 15min)
- Mark the webhook as 'failed' after 5 consecutive failures
- Stop sending events until you reactivate the webhook
Testing Webhooks
Tips for testing your webhook integration:
- Use ngrok or similar to expose localhost for testing
- Check Settings > Webhooks for delivery logs and error details
- Use the 'Test' button to send a sample payload
- Verify signature validation works before going to production
Need help? See the Zapier Guide or Make Guide for step-by-step integration tutorials, or contact support@titancard.com.
Ready to get started?
Create your digital business card in minutes. Join thousands of professionals already using TitanApp.
Get Started FreeWas this page helpful?