Skip to content

Webhooks

Webhooks allow your application to receive real-time HTTP callbacks when events occur in AuthStack. This enables you to build reactive integrations without polling the API.

Webhooks are configured at the application level. Each OAuth application can have multiple webhook subscriptions that trigger on specific events.

MethodEndpointDescription
GET/api/applications/{appId}/webhooksList all webhooks for an application
POST/api/applications/{appId}/webhooksCreate a new webhook
PUT/api/applications/{appId}/webhooks/{webhookId}Update a webhook
DELETE/api/applications/{appId}/webhooks/{webhookId}Delete a webhook
POST/api/applications/{appId}/webhooks/{webhookId}/regenerate-secretRegenerate webhook secret
GET/api/applications/{appId}/webhooks/{webhookId}/deliveriesGet delivery history
GET/api/applications/{appId}/webhooks/eventsList available event types
Event TypeDescription
user.createdFired when a new user registers
user.updatedFired when a user profile is updated
user.deletedFired when a user is deleted
POST /api/applications/{appId}/webhooks
Authorization: Bearer <access_token>
Content-Type: application/json
{
"url": "https://your-server.com/webhooks/authstack",
"events": ["user.created", "user.updated"],
"maxRetries": 3,
"retryDelaySeconds": 60
}
FieldTypeRequiredDescription
urlstringYesThe HTTPS URL to receive webhook payloads
eventsstring[]YesArray of event types to subscribe to
maxRetriesnumberNoMaximum retry attempts (default: 3)
retryDelaySecondsnumberNoDelay between retries in seconds (default: 60)
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"organizationId": "org-id",
"applicationId": "app-id",
"applicationName": "My App",
"url": "https://your-server.com/webhooks/authstack",
"secret": "whsec_abc123...",
"events": ["user.created", "user.updated"],
"isActive": true,
"maxRetries": 3,
"retryDelaySeconds": 60,
"createdAt": "2025-01-15T10:30:00Z"
}

When an event occurs, AuthStack sends a POST request to your webhook URL:

POST https://your-server.com/webhooks/authstack
Content-Type: application/json
X-Webhook-Signature: sha256=abc123...
X-Webhook-Event: user.created
X-Webhook-Delivery-Id: delivery-id
{
"id": "delivery-id",
"event": "user.created",
"timestamp": "2025-01-15T10:30:00Z",
"data": {
"userId": "user-id",
"email": "user@example.com",
"firstName": "John",
"lastName": "Doe"
}
}
HeaderDescription
X-Webhook-SignatureHMAC-SHA256 signature of the payload
X-Webhook-EventThe event type that triggered this delivery
X-Webhook-Delivery-IdUnique identifier for this delivery attempt

Always verify webhook signatures to ensure requests are from AuthStack.

const crypto = require('crypto');
function verifyWebhookSignature(payload, signature, secret) {
const expectedSignature = crypto
.createHmac('sha256', secret)
.update(payload)
.digest('hex');
const expected = `sha256=${expectedSignature}`;
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected)
);
}
// Express middleware
app.post('/webhooks/authstack', express.raw({ type: 'application/json' }), (req, res) => {
const signature = req.headers['x-webhook-signature'];
const payload = req.body.toString();
if (!verifyWebhookSignature(payload, signature, process.env.WEBHOOK_SECRET)) {
return res.status(401).send('Invalid signature');
}
const event = JSON.parse(payload);
switch (event.event) {
case 'user.created':
// Handle new user
break;
case 'user.updated':
// Handle user update
break;
}
res.status(200).send('OK');
});
import hmac
import hashlib
def verify_webhook_signature(payload: bytes, signature: str, secret: str) -> bool:
expected = hmac.new(
secret.encode(),
payload,
hashlib.sha256
).hexdigest()
return hmac.compare_digest(f"sha256={expected}", signature)
# Flask example
@app.route('/webhooks/authstack', methods=['POST'])
def handle_webhook():
signature = request.headers.get('X-Webhook-Signature')
payload = request.get_data()
if not verify_webhook_signature(payload, signature, WEBHOOK_SECRET):
return 'Invalid signature', 401
event = request.get_json()
if event['event'] == 'user.created':
# Handle new user
pass
return 'OK', 200
PUT /api/applications/{appId}/webhooks/{webhookId}
Authorization: Bearer <access_token>
Content-Type: application/json
{
"url": "https://your-server.com/webhooks/authstack-v2",
"events": ["user.created", "user.updated", "user.deleted"],
"isActive": true,
"maxRetries": 5,
"retryDelaySeconds": 120
}

If your webhook secret is compromised, regenerate it immediately:

POST /api/applications/{appId}/webhooks/{webhookId}/regenerate-secret
Authorization: Bearer <access_token>
{
"secret": "whsec_newSecret123..."
}

Monitor webhook deliveries and troubleshoot failures:

GET /api/applications/{appId}/webhooks/{webhookId}/deliveries?page=1&pageSize=50
Authorization: Bearer <access_token>
{
"items": [
{
"id": "delivery-id",
"webhookId": "webhook-id",
"event": "user.created",
"statusCode": 200,
"success": true,
"attemptNumber": 1,
"deliveredAt": "2025-01-15T10:30:00Z"
}
],
"totalCount": 150,
"page": 1,
"pageSize": 50
}

When a webhook delivery fails (non-2xx response or timeout):

  1. AuthStack waits for retryDelaySeconds
  2. Retries up to maxRetries times
  3. Each retry is logged in the delivery history

A delivery is considered failed if:

  • HTTP status code is not 2xx
  • Request times out (30 seconds)
  • Connection error occurs
  1. Always verify signatures - Never process unverified webhooks
  2. Respond quickly - Return 200 within 30 seconds to avoid timeouts
  3. Process asynchronously - Queue webhook events for background processing
  4. Handle duplicates - Use delivery IDs to deduplicate events
  5. Use HTTPS - Webhook URLs must use HTTPS in production
  6. Monitor deliveries - Check delivery history for failures
  7. Regenerate secrets periodically - Rotate secrets as a security practice
Status CodeDescription
400Invalid request (missing URL, invalid events)
401Unauthorized - invalid or expired token
403Forbidden - not authorized for this application
404Webhook or application not found
500Server error