Webhooks

Set up real-time notifications when invoice processing is complete.

Webhooks

Set up real-time notifications when invoice processing is complete.

Overview

Webhooks allow your application to receive real-time notifications when events occur in SmartBills. Instead of polling our API for updates, we'll send HTTP POST requests to your specified endpoint.

Setting Up Webhooks

1. Create an Endpoint

Create an endpoint in your application to receive webhook notifications:

// Express.js example
app.post('/webhooks/smartbills', (req, res) => {
  const event = req.body;
  
  switch (event.type) {
    case 'invoice.completed':
      handleInvoiceCompleted(event.data);
      break;
    case 'invoice.failed':
      handleInvoiceFailed(event.data);
      break;
  }
  
  res.status(200).send('OK');
});

2. Configure Webhook URL

You can set your webhook URL in two ways:

Option 1: Per Request Include the webhook_url parameter when creating an invoice:

::code-example{ curlExample="curl -X POST https://api.smartbills.com/v1/invoices/extract \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: multipart/form-data" \ -F "file=@invoice.pdf" \ -F "webhook_url=https://yourapp.com/webhooks/smartbills\""

javascriptExample="const formData = new FormData(); formData.append('file', fileInput.files0); formData.append('webhook_url', 'https://yourapp.com/webhooks/smartbills');

await fetch('https://api.smartbills.com/v1/invoices/extract', { method: 'POST', headers: { 'Authorization': 'Bearer YOUR_API_KEY' }, body: formData });"

pythonExample="import requests

files = {'file': open('invoice.pdf', 'rb')} data = {'webhook_url': 'https://yourapp.com/webhooks/smartbills'} headers = {'Authorization': 'Bearer YOUR_API_KEY'}

requests.post( 'https://api.smartbills.com/v1/invoices/extract', files=files, data=data, headers=headers )" } ::

Option 2: Default Webhook URL Set a default webhook URL in your dashboard that will be used for all requests.

Webhook Events

invoice.completed

Sent when invoice processing is successfully completed.

{
  "id": "evt_123456789",
  "type": "invoice.completed",
  "created_at": "2024-01-15T10:31:00Z",
  "data": {
    "id": "inv_123456789",
    "status": "completed",
    "created_at": "2024-01-15T10:30:00Z",
    "completed_at": "2024-01-15T10:30:45Z",
    "file_url": "https://files.smartbills.com/inv_123456789.pdf",
    "extracted_data": {
      "invoice_number": "INV-2024-001",
      "date": "2024-01-15",
      "total_amount": 1250.00,
      "currency": "USD",
      "vendor": {
        "name": "Acme Corp",
        "address": "123 Business St, City, State 12345"
      }
    }
  }
}

invoice.failed

Sent when invoice processing fails.

{
  "id": "evt_123456790",
  "type": "invoice.failed",
  "created_at": "2024-01-15T10:31:00Z",
  "data": {
    "id": "inv_123456789",
    "status": "failed",
    "created_at": "2024-01-15T10:30:00Z",
    "failed_at": "2024-01-15T10:30:30Z",
    "file_url": "https://files.smartbills.com/inv_123456789.pdf",
    "error": {
      "code": "UNREADABLE_TEXT",
      "message": "The text in the image is too blurry to read"
    }
  }
}

Webhook Signature Verification

To ensure webhooks are from SmartBills, we include a signature in the X-SmartBills-Signature header. Always verify this signature.

Verification Process

  1. Get your webhook secret from the dashboard
  2. Create a hash using HMAC SHA-256
  3. Compare with the provided signature

Example Verification

const crypto = require('crypto');

function verifyWebhookSignature(payload, signature, secret) {
  const expectedSignature = crypto
    .createHmac('sha256', secret)
    .update(payload, 'utf8')
    .digest('hex');
  
  return `sha256=${expectedSignature}` === signature;
}

// Express.js middleware
app.use('/webhooks/smartbills', express.raw({ type: 'application/json' }));

app.post('/webhooks/smartbills', (req, res) => {
  const signature = req.headers['x-smartbills-signature'];
  const payload = req.body;
  
  if (!verifyWebhookSignature(payload, signature, process.env.WEBHOOK_SECRET)) {
    return res.status(401).send('Unauthorized');
  }
  
  // Process the webhook
  const event = JSON.parse(payload);
  // ... handle event
  
  res.status(200).send('OK');
});
import hmac
import hashlib
import json

def verify_webhook_signature(payload, signature, secret):
    expected_signature = hmac.new(
        secret.encode('utf-8'),
        payload,
        hashlib.sha256
    ).hexdigest()
    
    return f"sha256={expected_signature}" == signature

# Flask example
from flask import Flask, request

@app.route('/webhooks/smartbills', methods=['POST'])
def handle_webhook():
    signature = request.headers.get('X-SmartBills-Signature')
    payload = request.get_data()
    
    if not verify_webhook_signature(payload, signature, WEBHOOK_SECRET):
        return 'Unauthorized', 401
    
    event = json.loads(payload)
    # ... handle event
    
    return 'OK', 200

Best Practices

Respond Quickly

Your webhook endpoint should respond with a 2xx status code within 10 seconds. Long-running tasks should be queued for background processing.

app.post('/webhooks/smartbills', (req, res) => {
  // Respond immediately
  res.status(200).send('OK');
  
  // Process in background
  backgroundQueue.add('process-webhook', req.body);
});

Handle Retries

If your endpoint doesn't respond with a 2xx status code, we'll retry the webhook:

  • Initial retry after 1 minute
  • Then every 5 minutes for 1 hour
  • Then every hour for 24 hours
  • Maximum of 20 retry attempts

Idempotency

Webhooks may be delivered more than once. Use the id field to ensure idempotent processing:

const processedEvents = new Set();

app.post('/webhooks/smartbills', (req, res) => {
  const event = req.body;
  
  if (processedEvents.has(event.id)) {
    return res.status(200).send('Already processed');
  }
  
  processedEvents.add(event.id);
  // Process the event...
  
  res.status(200).send('OK');
});

Testing Webhooks

Use tools like ngrok to test webhooks locally:

# Install ngrok
npm install -g ngrok

# Expose your local server
ngrok http 3000

# Use the generated URL as your webhook endpoint
# https://abc123.ngrok.io/webhooks/smartbills

Webhook Management

Dashboard Configuration

Manage your webhooks in the SmartBills dashboard:

  1. Go to Settings > Webhooks
  2. Add your webhook URL
  3. Select the events you want to receive
  4. Copy your webhook secret for signature verification

API Management

You can also manage webhooks via API:

POST/v1/webhooks
POST

Create a new webhook endpoint

{
  "url": "https://yourapp.com/webhooks/smartbills",
  "events": ["invoice.completed", "invoice.failed"],
  "description": "Production webhook"
}

Troubleshooting

Common Issues

Webhooks not being received

  • Check that your endpoint is publicly accessible
  • Verify the URL is correct and returns 2xx status codes
  • Ensure your server can handle HTTPS requests

Signature verification failing

  • Double-check your webhook secret
  • Ensure you're using the raw request body for verification
  • Verify the HMAC implementation

Timeouts

  • Optimize your webhook handler to respond quickly
  • Move heavy processing to background jobs
  • Consider using a webhook processing service

Testing Your Endpoint

Test your webhook endpoint manually:

curl -X POST https://yourapp.com/webhooks/smartbills \
  -H "Content-Type: application/json" \
  -H "X-SmartBills-Signature: sha256=test_signature" \
  -d '{
    "id": "evt_test",
    "type": "invoice.completed",
    "created_at": "2024-01-15T10:31:00Z",
    "data": {
      "id": "inv_test",
      "status": "completed"
    }
  }'

Next Steps