Set up real-time notifications when invoice processing is complete.
Set up real-time notifications when invoice processing is complete.
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.
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');
});
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.
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"
}
}
}
}
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"
}
}
}
To ensure webhooks are from SmartBills, we include a signature in the X-SmartBills-Signature
header. Always verify this signature.
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
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);
});
If your endpoint doesn't respond with a 2xx status code, we'll retry the webhook:
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');
});
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
Manage your webhooks in the SmartBills dashboard:
You can also manage webhooks via API:
/v1/webhooks
Create a new webhook endpoint
{
"url": "https://yourapp.com/webhooks/smartbills",
"events": ["invoice.completed", "invoice.failed"],
"description": "Production webhook"
}
Webhooks not being received
Signature verification failing
Timeouts
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"
}
}'