Outgoing Webhooks
Outgoing webhooks allow GxP to send HTTP callbacks to your servers when specific events occur.
Overview
Configure outgoing webhooks to:
- Sync data to external systems in real-time
- Trigger workflows in other applications
- Build integrations with CRMs, marketing tools, etc.
- Create audit logs in external systems
Subscribing to Webhooks
Via API
curl -X POST "https://api.gramercy.cloud/api/v1/webhook/subscribe" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{
"url": "https://your-server.com/webhooks/gxp",
"events": ["attendee.created", "attendee.updated", "access.granted"],
"project_id": 123,
"secret": "your-webhook-secret"
}'
Response
{
"data": {
"id": "wh_abc123",
"url": "https://your-server.com/webhooks/gxp",
"events": ["attendee.created", "attendee.updated", "access.granted"],
"project_id": 123,
"is_active": true,
"created_at": "2024-01-15T10:00:00Z"
}
}
Unsubscribing
curl -X DELETE "https://api.gramercy.cloud/api/v1/webhook/unsubscribe/wh_abc123" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Accept: application/json"
Available Events
Attendee Events
| Event | Triggered When |
|---|---|
attendee.created | New attendee is created |
attendee.updated | Attendee data is modified |
attendee.deleted | Attendee is removed |
Access Control Events
| Event | Triggered When |
|---|---|
access.granted | Access is granted at an access point |
access.denied | Access is denied at an access point |
Form Events
| Event | Triggered When |
|---|---|
form.response.created | New form response is submitted |
form.response.updated | Form response is modified |
Credential Events
| Event | Triggered When |
|---|---|
credential.created | Credential is assigned to attendee |
credential.revoked | Credential is removed from attendee |
Webhook Payload Format
All webhooks follow a standard envelope format:
{
"id": "evt_abc123",
"event": "attendee.created",
"timestamp": "2024-01-15T10:30:00Z",
"project_id": 123,
"data": {
// Event-specific payload
}
}
Payload Examples
attendee.created
{
"id": "evt_abc123",
"event": "attendee.created",
"timestamp": "2024-01-15T10:30:00Z",
"project_id": 123,
"data": {
"attendee_id": 12345,
"first_name": "Jane",
"last_name": "Smith",
"email": "jane@example.com",
"attendee_type_id": 1,
"created_at": "2024-01-15T10:30:00Z"
}
}
access.granted
{
"id": "evt_def456",
"event": "access.granted",
"timestamp": "2024-01-15T14:30:00Z",
"project_id": 123,
"data": {
"attendee_id": 12345,
"access_point_id": 1,
"access_zone_id": 2,
"credential_id": 100,
"granted_at": "2024-01-15T14:30:00Z"
}
}
access.denied
{
"id": "evt_ghi789",
"event": "access.denied",
"timestamp": "2024-01-15T14:31:00Z",
"project_id": 123,
"data": {
"credential_value": "INVALID123",
"access_point_id": 1,
"reason": "invalid_credential",
"denied_at": "2024-01-15T14:31:00Z"
}
}
Signature Verification
All outgoing webhooks include an HMAC signature for verification.
Headers
| Header | Description |
|---|---|
X-GxP-Signature | HMAC-SHA256 signature of the payload |
X-GxP-Event | Event type (e.g., attendee.created) |
X-GxP-Delivery-ID | Unique delivery identifier |
X-GxP-Timestamp | Unix timestamp of the request |
Verifying Signatures
// PHP
function verifyWebhook($payload, $signature, $secret) {
$expectedSignature = 'sha256=' . hash_hmac('sha256', $payload, $secret);
return hash_equals($expectedSignature, $signature);
}
$payload = file_get_contents('php://input');
$signature = $_SERVER['HTTP_X_GXP_SIGNATURE'];
if (!verifyWebhook($payload, $signature, $webhookSecret)) {
http_response_code(401);
exit('Invalid signature');
}
$event = json_decode($payload, true);
// Process event...
// Node.js
const crypto = require('crypto');
function verifyWebhook(payload, signature, secret) {
const expectedSignature = 'sha256=' +
crypto.createHmac('sha256', secret)
.update(payload)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expectedSignature)
);
}
app.post('/webhooks/gxp', (req, res) => {
const payload = JSON.stringify(req.body);
const signature = req.headers['x-gxp-signature'];
if (!verifyWebhook(payload, signature, webhookSecret)) {
return res.status(401).send('Invalid signature');
}
// Process event...
res.status(200).send('OK');
});
Retry Policy
If your endpoint fails to respond, GxP will retry:
| Attempt | Delay |
|---|---|
| 1 | Immediate |
| 2 | 1 minute |
| 3 | 5 minutes |
| 4 | 30 minutes |
| 5 | 2 hours |
After 5 failed attempts, the delivery is marked as failed.
Success Criteria
A delivery is considered successful when:
- HTTP status code is 2xx
- Response is received within 30 seconds
Idempotency
Use the X-GxP-Delivery-ID header to handle duplicate deliveries:
const deliveryId = req.headers['x-gxp-delivery-id'];
// Check if already processed
if (await isDeliveryProcessed(deliveryId)) {
return res.status(200).send('Already processed');
}
// Process and mark as processed
await processWebhook(req.body);
await markDeliveryProcessed(deliveryId);
Filtering Events
Subscribe to specific event subtypes:
{
"url": "https://your-server.com/webhooks/gxp",
"events": [
"attendee.created",
"access.granted"
],
"filters": {
"attendee_type_id": [1, 2],
"access_zone_id": [5]
}
}
Debugging Webhooks
View Delivery History
In the dashboard:
- Navigate to Project Settings > Webhooks
- Select your webhook subscription
- Click View Deliveries
Delivery Log Entry
{
"id": "del_abc123",
"event_id": "evt_abc123",
"event_type": "attendee.created",
"url": "https://your-server.com/webhooks/gxp",
"status": "delivered",
"response_code": 200,
"response_time_ms": 150,
"attempts": 1,
"delivered_at": "2024-01-15T10:30:05Z"
}
Testing Your Endpoint
Use the webhook testing tool in the dashboard:
- Navigate to Project Settings > Webhooks
- Select your webhook subscription
- Click Test Webhook
- Choose an event type to send
Best Practices
- Respond quickly: Return 200 OK as soon as possible, process async
- Verify signatures: Always validate the HMAC signature
- Handle duplicates: Use delivery IDs for idempotency
- Log everything: Keep records of all received webhooks
- Monitor failures: Set up alerts for failed deliveries
- Use HTTPS: Always use secure endpoints
- Handle retries gracefully: Your endpoint may receive the same event multiple times
Error Handling
Common Issues
| Issue | Solution |
|---|---|
| Timeout | Process events asynchronously |
| Invalid signature | Check your webhook secret |
| SSL certificate error | Ensure valid SSL certificate |
| Connection refused | Check firewall and server status |
Disabling Webhooks
Webhooks are automatically disabled after:
- 100 consecutive failures
- 7 days of failures
Re-enable in the dashboard after fixing the issue.