Webhooks

Receive real-time notifications for events in your AccessIQ organization. Build integrations that react to user, organization, and security events.

Endpoints

MethodEndpointDescription
GET/webhooksList webhooks
GET/webhooks/:idGet webhook
POST/webhooksCreate webhook
PATCH/webhooks/:idUpdate webhook
DELETE/webhooks/:idDelete webhook
POST/webhooks/:id/testSend test event

Create Webhook

POST /v1/organizations/:org/webhooksbash
curl -X POST https://api.accessiq.io/v1/organizations/acme-corp/webhooks \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "User Events",
    "url": "https://your-app.com/webhooks/accessiq",
    "events": [
      "user.created",
      "user.updated",
      "user.deleted",
      "user.login"
    ],
    "enabled": true,
    "secret": "whsec_your_secret_key"
  }'

# Response (201 Created)
{
  "data": {
    "id": "wh_abc123",
    "name": "User Events",
    "url": "https://your-app.com/webhooks/accessiq",
    "events": ["user.created", "user.updated", "user.deleted", "user.login"],
    "enabled": true,
    "secret": "whsec_your_secret_key",
    "createdAt": "2024-01-15T12:00:00Z"
  }
}

Available Events

User Events

  • user.created - New user created
  • user.updated - User profile updated
  • user.deleted - User deleted
  • user.suspended - User suspended
  • user.activated - User activated
  • user.login - User logged in
  • user.login_failed - Login attempt failed
  • user.logout - User logged out
  • user.password_changed - Password changed
  • user.mfa_enabled - MFA enabled
  • user.mfa_disabled - MFA disabled

Organization Events

  • organization.created - Organization created
  • organization.updated - Organization updated
  • organization.deleted - Organization deleted
  • organization.member_added - Member added
  • organization.member_removed - Member removed

Role Events

  • role.created - Role created
  • role.updated - Role updated
  • role.deleted - Role deleted
  • role.assigned - Role assigned to user
  • role.revoked - Role revoked from user

Security Events

  • security.suspicious_activity - Suspicious activity detected
  • security.api_key_created - API key created
  • security.api_key_revoked - API key revoked
  • security.session_revoked - Session revoked
  • security.break_glass_requested - Emergency access requested

Webhook Payload

Example Webhook Payloadjson
{
  "id": "evt_abc123",
  "type": "user.created",
  "timestamp": "2024-01-15T12:00:00Z",
  "organization": {
    "id": "org_acme",
    "name": "Acme Corp"
  },
  "data": {
    "id": "user_def456",
    "email": "jane@acme.com",
    "firstName": "Jane",
    "lastName": "Smith",
    "status": "pending",
    "createdAt": "2024-01-15T12:00:00Z"
  },
  "actor": {
    "type": "user",
    "id": "user_abc123",
    "email": "admin@acme.com"
  },
  "metadata": {
    "ipAddress": "192.168.1.100",
    "userAgent": "Mozilla/5.0...",
    "source": "api"
  }
}

Signature Verification

Verify webhook signatures to ensure requests come from AccessIQ:

Node.js Signature Verificationjavascript
const crypto = require('crypto');

function verifyWebhookSignature(payload, signature, secret) {
  const timestamp = signature.split(',')[0].split('=')[1];
  const receivedSig = signature.split(',')[1].split('=')[1];

  // Check timestamp is within 5 minutes
  const currentTime = Math.floor(Date.now() / 1000);
  if (currentTime - parseInt(timestamp) > 300) {
    throw new Error('Webhook timestamp too old');
  }

  // Verify signature
  const signedPayload = `${timestamp}.${JSON.stringify(payload)}`;
  const expectedSig = crypto
    .createHmac('sha256', secret)
    .update(signedPayload)
    .digest('hex');

  if (receivedSig !== expectedSig) {
    throw new Error('Invalid webhook signature');
  }

  return true;
}

// Express middleware
app.post('/webhooks/accessiq', (req, res) => {
  const signature = req.headers['accessiq-signature'];

  try {
    verifyWebhookSignature(req.body, signature, process.env.WEBHOOK_SECRET);

    // Process the webhook
    const { type, data } = req.body;
    console.log(`Received ${type} event`, data);

    res.status(200).json({ received: true });
  } catch (error) {
    console.error('Webhook verification failed:', error.message);
    res.status(400).json({ error: error.message });
  }
});
Always Verify Signatures
Never process webhook payloads without verifying the signature. This prevents attackers from sending fake events to your endpoint.

Retry Policy

If your endpoint returns an error (non-2xx status), AccessIQ will retry:

AttemptDelay
1st retry1 minute
2nd retry5 minutes
3rd retry30 minutes
4th retry2 hours
5th retry (final)24 hours

Webhook Logs

View delivery attempts, response codes, and retry history in the dashboard under Settings → Webhooks → Logs.

Test Webhook

POST /v1/organizations/:org/webhooks/:id/testbash
curl -X POST https://api.accessiq.io/v1/organizations/acme-corp/webhooks/wh_abc123/test \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "eventType": "user.created"
  }'

# Response
{
  "success": true,
  "deliveryId": "del_xyz789",
  "responseCode": 200,
  "responseTime": 245,
  "responseBody": "{ \"received\": true }"
}

Webhook Schema

Webhook Objecttypescript
interface Webhook {
  id: string;
  name: string;
  url: string;
  events: string[];
  enabled: boolean;
  secret: string;
  headers?: Record<string, string>;
  metadata?: {
    lastDelivery?: string;
    successRate?: number;
    failureCount?: number;
  };
  createdAt: string;
  updatedAt: string;
}

interface WebhookEvent {
  id: string;
  type: string;
  timestamp: string;
  organization: {
    id: string;
    name: string;
  };
  data: Record<string, any>;
  actor?: {
    type: 'user' | 'system' | 'api';
    id?: string;
    email?: string;
  };
  metadata?: Record<string, any>;
}
Best Practices
  • Always return 2xx quickly, then process async
  • Implement idempotency using the event ID
  • Store webhook secrets securely
  • Monitor webhook health in your observability stack