Skip to content

Webhooks ​

Real-time Event Notifications ​

Webhooks allow you to receive real-time notifications when events occur in your AImyChats account. Instead of polling the API, AImyChats will send HTTP POST requests to your specified endpoint.

Setting Up Webhooks ​

Via Dashboard ​

  1. Log in to your AImyChats account
  2. Navigate to Settings → Webhooks
  3. Click "Add Webhook"
  4. Enter your endpoint URL
  5. Select events to subscribe to
  6. Save and test

Via API ​

javascript
POST /v1/webhooks

{
  "url": "https://your-app.com/webhooks/devify",
  "events": ["conversation.created", "summary.generated"],
  "description": "Production webhook",
  "active": true
}

Response:

json
{
  "id": "webhook_123",
  "url": "https://your-app.com/webhooks/devify",
  "events": ["conversation.created", "summary.generated"],
  "secret": "whsec_abc123...",
  "active": true,
  "created_at": "2024-01-01T00:00:00Z"
}

WARNING

Save the secret value! You'll need it to verify webhook signatures.

Available Events ​

Conversation Events ​

EventDescription
conversation.createdNew conversation created
conversation.updatedConversation updated
conversation.deletedConversation deleted

Summary Events ​

EventDescription
summary.generatedAI summary completed
summary.failedAI summary generation failed

Storage Events ​

EventDescription
storage.limit_reachedStorage quota 80% full
storage.limit_exceededStorage quota exceeded

Account Events ​

EventDescription
subscription.updatedSubscription plan changed
subscription.cancelledSubscription cancelled
api_key.createdNew API key created
api_key.revokedAPI key revoked

Webhook Payload ​

Structure ​

All webhook payloads follow this structure:

json
{
  "id": "evt_123",
  "type": "conversation.created",
  "created_at": "2024-01-01T00:00:00Z",
  "data": {
    // Event-specific data
  }
}

Example Payloads ​

conversation.created:

json
{
  "id": "evt_abc123",
  "type": "conversation.created",
  "created_at": "2024-01-01T00:00:00Z",
  "data": {
    "conversation": {
      "id": "conv_123",
      "title": "Customer Inquiry",
      "source": "email",
      "created_at": "2024-01-01T00:00:00Z",
      "summary": null,
      "status": "processing"
    }
  }
}

summary.generated:

json
{
  "id": "evt_def456",
  "type": "summary.generated",
  "created_at": "2024-01-01T00:05:00Z",
  "data": {
    "conversation_id": "conv_123",
    "summary": {
      "title": "Product inquiry about features",
      "content": "Customer asked about pricing and features...",
      "sentiment": "positive",
      "topics": ["pricing", "features"],
      "action_items": ["Send pricing sheet", "Schedule demo"]
    }
  }
}

storage.limit_reached:

json
{
  "id": "evt_ghi789",
  "type": "storage.limit_reached",
  "created_at": "2024-01-01T10:00:00Z",
  "data": {
    "usage": {
      "current_mb": 160,
      "limit_mb": 200,
      "percentage": 80
    },
    "recommendation": "Consider upgrading to a higher plan"
  }
}

Webhook Security ​

Signature Verification ​

All webhooks include a signature in the X-AImyChats-Signature header. Verify this signature to ensure the webhook is from AImyChats.

Signature format:

t=1609459200,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd

Verification steps:

  1. Extract timestamp t and signature v1
  2. Construct signed payload: {timestamp}.{raw_body}
  3. Compute HMAC SHA256 using your webhook secret
  4. Compare computed signature with v1

Node.js example:

javascript
const crypto = require('crypto')

function verifyWebhook(payload, signature, secret) {
  const parts = signature.split(',')
  const timestamp = parts[0].split('=')[1]
  const expectedSig = parts[1].split('=')[1]

  const signedPayload = `${timestamp}.${payload}`
  const computedSig = crypto
    .createHmac('sha256', secret)
    .update(signedPayload)
    .digest('hex')

  return crypto.timingSafeEqual(
    Buffer.from(expectedSig),
    Buffer.from(computedSig)
  )
}

// Express.js middleware
app.post('/webhooks/aimychats', (req, res) => {
  const signature = req.headers['x-aimychats-signature']
  const payload = JSON.stringify(req.body)
  const secret = process.env.WEBHOOK_SECRET

  if (!verifyWebhook(payload, signature, secret)) {
    return res.status(401).send('Invalid signature')
  }

  // Process webhook
  const event = req.body
  console.log('Received event:', event.type)

  res.status(200).send('OK')
})

Python example:

python
import hmac
import hashlib
import time

def verify_webhook(payload, signature, secret):
    parts = signature.split(',')
    timestamp = parts[0].split('=')[1]
    expected_sig = parts[1].split('=')[1]

    signed_payload = f"{timestamp}.{payload}"
    computed_sig = hmac.new(
        secret.encode(),
        signed_payload.encode(),
        hashlib.sha256
    ).hexdigest()

    return hmac.compare_digest(expected_sig, computed_sig)

# Flask example
@app.route('/webhooks/aimychats', methods=['POST'])
def handle_webhook():
    signature = request.headers.get('X-AImyChats-Signature')
    payload = request.get_data(as_text=True)
    secret = os.environ['WEBHOOK_SECRET']

    if not verify_webhook(payload, signature, secret):
        return 'Invalid signature', 401

    event = request.json
    print(f"Received event: {event['type']}")

    return 'OK', 200

Replay Attack Prevention ​

Check the timestamp in the signature to prevent replay attacks:

javascript
function isRecentWebhook(signature, toleranceSeconds = 300) {
  const timestamp = parseInt(signature.split(',')[0].split('=')[1])
  const now = Math.floor(Date.now() / 1000)
  return Math.abs(now - timestamp) <= toleranceSeconds
}

Handling Webhooks ​

Best Practices ​

  1. Respond quickly: Return 200 OK within 5 seconds
  2. Process asynchronously: Queue webhook for background processing
  3. Handle duplicates: Webhooks may be sent multiple times
  4. Log everything: Keep detailed logs for debugging

Example with queue:

javascript
const queue = require('bull')
const webhookQueue = new queue('webhooks')

app.post('/webhooks/devify', async (req, res) => {
  // Verify signature
  if (!verifyWebhook(...)) {
    return res.status(401).send('Invalid signature')
  }

  // Queue for processing
  await webhookQueue.add(req.body)

  // Respond immediately
  res.status(200).send('OK')
})

// Process in background
webhookQueue.process(async (job) => {
  const event = job.data

  switch (event.type) {
    case 'conversation.created':
      await handleNewConversation(event.data)
      break
    case 'summary.generated':
      await handleNewSummary(event.data)
      break
    default:
      console.log(`Unhandled event: ${event.type}`)
  }
})

Idempotency ​

Use the event id to handle duplicate webhooks:

javascript
const processedEvents = new Set()

async function processWebhook(event) {
  if (processedEvents.has(event.id)) {
    console.log('Duplicate event, skipping')
    return
  }

  processedEvents.add(event.id)

  // Process event
  await handleEvent(event)
}

Retry Logic ​

Automatic Retries ​

If your endpoint fails to respond with 200 OK, Devify will retry:

  • First retry: After 1 minute
  • Second retry: After 5 minutes
  • Third retry: After 30 minutes
  • Fourth retry: After 2 hours
  • Fifth retry: After 6 hours

After 5 failed attempts, the webhook is marked as failed.

Manual Retry ​

Retry failed webhooks via dashboard or API:

javascript
POST /v1/webhooks/{webhook_id}/events/{event_id}/retry

Testing Webhooks ​

Test Endpoint ​

Send a test webhook to verify your setup:

javascript
POST /v1/webhooks/{webhook_id}/test

{
  "event_type": "conversation.created"
}

Local Testing ​

Use tools like ngrok for local development:

bash
# Start ngrok
ngrok http 3000

# Use the ngrok URL as your webhook URL
https://abc123.ngrok.io/webhooks/devify

Webhook Logs ​

View webhook delivery logs in the dashboard:

  • Request payload
  • Response status
  • Response time
  • Error messages

Managing Webhooks ​

List Webhooks ​

javascript
GET /v1/webhooks

Update Webhook ​

javascript
PATCH /v1/webhooks/{webhook_id}

{
  "events": ["conversation.created"],
  "active": false
}

Delete Webhook ​

javascript
DELETE /v1/webhooks/{webhook_id}

Troubleshooting ​

Webhook Not Received ​

  1. Check webhook is active
  2. Verify endpoint URL is correct
  3. Check firewall settings
  4. Review webhook logs in dashboard

Signature Verification Fails ​

  1. Use correct webhook secret
  2. Don't modify request body before verification
  3. Check timestamp tolerance

Timeout Issues ​

  1. Return 200 OK immediately
  2. Process webhooks asynchronously
  3. Increase server timeout settings

Rate Limits ​

Webhook deliveries don't count towards your API rate limits. However, we limit:

  • Maximum 10 webhooks per account
  • Maximum 1000 events per hour per webhook

Examples ​

Slack Notification ​

javascript
async function sendSlackNotification(event) {
  if (event.type === 'summary.generated') {
    await fetch(SLACK_WEBHOOK_URL, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        text: `New summary: ${event.data.summary.title}`,
        blocks: [
          {
            type: 'section',
            text: {
              type: 'mrkdwn',
              text: `*${event.data.summary.title}*\n${event.data.summary.content}`
            }
          }
        ]
      })
    })
  }
}

Database Sync ​

javascript
async function syncToDatabase(event) {
  if (event.type === 'conversation.created') {
    await db.conversations.insert({
      devify_id: event.data.conversation.id,
      title: event.data.conversation.title,
      created_at: event.data.conversation.created_at
    })
  }
}

Next Steps ​

Support ​

Need help with webhooks? Contact support or check our FAQ.