InboxIssue

Webhooks

Receive real-time notifications for test results

Webhooks

Webhooks allow you to receive real-time HTTP notifications when spam test results arrive.

Setting Up Webhooks

1. Create a Webhook

  1. Go to Settings > Webhooks
  2. Click Add Webhook
  3. Configure the webhook:
FieldDescription
URLYour endpoint URL (HTTPS recommended)
Webhook TypeEvent type to receive
Include Raw HeadersInclude full email headers
Include Raw EmailInclude complete email content
  1. Click Create Webhook

2. Webhook Types

TypeDescription
Spam TestTest result notifications

Payload Format

When a test result arrives, InboxIssue sends a POST request to your webhook URL.

Headers

HeaderDescription
Content-Typeapplication/json
X-InboxIssue-SignatureHMAC-SHA256 signature
X-InboxIssue-EventEvent type
X-InboxIssue-DeliveryUnique delivery ID

Body

{
  "event_type": "test_result.delivered",
  "timestamp": "2024-01-15T10:32:45Z",
  "idempotency_key": "tr_98765_1705315965",
  "spam_test": {
    "key": "inboxissue-12345-2024-01-15-inboxissue",
    "id": 12345,
    "name": "Newsletter Test",
    "created_at": "2024-01-15T10:30:00Z",
    "sender_email_address": "newsletter@company.com",
    "subject": "Your Q4 Newsletter",
    "source": "api",
    "metadata": {
      "campaign_id": "camp_123"
    }
  },
  "test_result": {
    "id": 98765,
    "email_provider_id": 42,
    "provider_name": "gmail.com",
    "provider_type": "consumer",
    "recipient_email": "test@gmail.com",
    "delivery": {
      "status": "delivered",
      "folder": "INBOX",
      "delivery_time_seconds": 165,
      "is_inbox": true,
      "is_spam": false
    },
    "authentication": {
      "spf": {
        "pass": true,
        "result": "pass",
        "details": "sender IP authorized"
      },
      "dkim": {
        "pass": true,
        "result": "pass",
        "details": "signature verified"
      },
      "dmarc": {
        "pass": true,
        "result": "pass",
        "action": "none"
      },
      "comp_auth": {
        "result": "pass",
        "reason_code": 200
      }
    },
    "spam_scores": {
      "microsoft_scl": {
        "score": 1,
        "max": 9,
        "interpretation": "Very low spam probability"
      },
      "microsoft_bcl": {
        "score": 0,
        "max": 9,
        "interpretation": "Not bulk mail"
      }
    }
  },
  "test_progress": {
    "total_providers": 10,
    "completed": 7,
    "pending": 3,
    "inbox_count": 5,
    "spam_count": 2,
    "is_complete": false
  }
}

Signature Verification

Verify webhook authenticity using the signature header.

Ruby

def verify_signature(payload, signature, secret)
  expected = OpenSSL::HMAC.hexdigest('SHA256', secret, payload)
  Rack::Utils.secure_compare(expected, signature)
end
 
# In your controller
def webhook
  payload = request.raw_post
  signature = request.headers['X-InboxIssue-Signature']
  secret = ENV['INBOXISSUE_WEBHOOK_SECRET']
 
  unless verify_signature(payload, signature, secret)
    return head :unauthorized
  end
 
  # Process webhook
  data = JSON.parse(payload)
  # ...
end

Python

import hmac
import hashlib
from flask import Flask, request
 
app = Flask(__name__)
 
def verify_signature(payload: bytes, signature: str, secret: str) -> bool:
    expected = hmac.new(
        secret.encode(),
        payload,
        hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(expected, signature)
 
@app.route('/webhook', methods=['POST'])
def webhook():
    payload = request.get_data()
    signature = request.headers.get('X-InboxIssue-Signature')
    secret = os.environ['INBOXISSUE_WEBHOOK_SECRET']
 
    if not verify_signature(payload, signature, secret):
        return 'Unauthorized', 401
 
    data = request.get_json()
    # Process webhook
    return 'OK', 200

JavaScript (Node.js)

const crypto = require('crypto');
const express = require('express');
 
const app = express();
app.use(express.raw({ type: 'application/json' }));
 
function verifySignature(payload, signature, secret) {
  const expected = crypto
    .createHmac('sha256', secret)
    .update(payload)
    .digest('hex');
  return crypto.timingSafeEqual(
    Buffer.from(expected),
    Buffer.from(signature)
  );
}
 
app.post('/webhook', (req, res) => {
  const payload = req.body;
  const signature = req.headers['x-inboxissue-signature'];
  const secret = process.env.INBOXISSUE_WEBHOOK_SECRET;
 
  if (!verifySignature(payload, signature, secret)) {
    return res.status(401).send('Unauthorized');
  }
 
  const data = JSON.parse(payload.toString());
  // Process webhook
  res.status(200).send('OK');
});

Retry Policy

Failed webhook deliveries are retried with exponential backoff:

AttemptDelay
1Immediate
21 minute
35 minutes
430 minutes
52 hours

After 5 failed attempts, the webhook delivery is abandoned.

Failure Conditions

Status CodeConsidered
2xxSuccess
3xxFailure (no follow)
4xxFailure
5xxFailure (retry)
Timeout (30s)Failure (retry)

Webhook Events

View recent webhook deliveries in Settings > Webhooks > Events.

ColumnDescription
EventEvent type
StatusSuccess, Failed, Pending
AttemptsNumber of delivery attempts
ResponseHTTP response code
TimestampWhen the event occurred

Best Practices

1. Respond Quickly

Return a 200 response immediately, then process asynchronously:

@app.route('/webhook', methods=['POST'])
def webhook():
    # Verify signature first
 
    # Queue for async processing
    queue.enqueue(process_webhook, request.get_json())
 
    return 'OK', 200  # Respond immediately

2. Handle Duplicates

Use the idempotency_key to detect and ignore duplicate deliveries:

def process_webhook(data):
    key = data['idempotency_key']
 
    if already_processed(key):
        return  # Skip duplicate
 
    mark_as_processed(key)
    # Process webhook

3. Use HTTPS

Always use HTTPS for webhook endpoints to encrypt data in transit.

4. Validate Signatures

Always verify the webhook signature before processing.

5. Monitor Delivery

Check webhook events regularly for failures.

Troubleshooting

Webhooks Not Arriving

  1. Check webhook URL is correct and accessible
  2. Verify your server is responding with 2xx
  3. Check webhook events for error messages
  4. Ensure firewall allows InboxIssue IPs

Signature Verification Failing

  1. Verify you're using the correct webhook secret
  2. Check that you're comparing the raw payload bytes
  3. Ensure no middleware is modifying the payload

Timeouts

Webhooks timeout after 30 seconds. If processing takes longer:

  1. Respond immediately with 200
  2. Process asynchronously
  3. Use a queue for heavy processing