Webhooks API
Configure webhooks to receive real-time notifications about transaction events, device status changes, and compliance alerts.
Overview
Webhooks allow your application to receive real-time HTTP notifications when events occur in your C-Tax account. Instead of polling for updates, C-Tax will push data to your server as soon as events happen.
Webhook Events
Transaction Events
| Event | Description |
|---|---|
transaction.created | Transaction has been created |
transaction.processing | Transaction is being processed by KRA |
transaction.completed | Transaction successfully completed |
transaction.failed | Transaction processing failed |
transaction.retrying | Transaction is being retried |
Device Events
| Event | Description |
|---|---|
device.registered | New VSCU device registered |
device.initialized | VSCU device initialized successfully |
device.connection_lost | Lost connection to VSCU device |
device.connection_restored | Connection to VSCU device restored |
device.error | VSCU device error occurred |
Branch Events
| Event | Description |
|---|---|
branch.created | New branch created |
branch.updated | Branch information updated |
branch.deleted | Branch deleted |
Compliance Events
| Event | Description |
|---|---|
compliance.warning | Compliance warning issued |
compliance.sync_required | Code data sync required |
Create Webhook
Register a new webhook endpoint.
/api/v1/webhooksCreate Webhook
Register a new webhook endpoint to receive real-time notifications for specified events.
Field Descriptions
| Field | Type | Required | Description |
|---|---|---|---|
url | string | Yes | Webhook endpoint URL (HTTPS recommended) |
events | array | Yes | Events to subscribe to |
headers | object | No | Custom headers to include in webhook requests |
Response
Status: 201 Created
{
"status": "success",
"message": "Webhook created successfully",
"data": {
"id": 1,
"url": "https://your-server.com/webhooks/ctax",
"events": [
"transaction.completed",
"transaction.failed",
"device.error"
],
"secret": "whsec_abc123def456...",
"is_active": true,
"created_at": "2025-01-15T14:30:00Z"
}
}Important: The secret is only returned once during creation. Store it securely for signature verification.
List Webhooks
Get all webhook configurations.
/api/v1/webhooksList Webhooks
Retrieve all webhook configurations for your account.
Response
Status: 200 OK
{
"status": "success",
"data": [
{
"id": 1,
"url": "https://your-server.com/webhooks/ctax",
"events": ["transaction.completed", "transaction.failed"],
"is_active": true,
"total_deliveries": 156,
"created_at": "2025-01-15T14:30:00Z",
"updated_at": "2025-01-15T14:30:00Z"
}
],
"meta": {
"total": 1,
"timestamp": "2025-01-15T14:30:00Z"
}
}Get Webhook Details
Retrieve a specific webhook configuration.
/api/v1/webhooks/{id}Get Webhook Details
Retrieve detailed information about a specific webhook configuration.
id*Webhook ID
Response
Status: 200 OK
{
"status": "success",
"data": {
"id": 1,
"url": "https://your-server.com/webhooks/ctax",
"events": ["transaction.completed", "transaction.failed"],
"is_active": true,
"headers": {
"X-Custom-Header": "custom-value"
},
"created_at": "2025-01-15T14:30:00Z",
"updated_at": "2025-01-15T14:30:00Z"
}
}Get Available Events
List all available webhook events.
/api/v1/webhooks/eventsGet Available Events
List all available webhook events you can subscribe to.
Response
Status: 200 OK
{
"status": "success",
"data": {
"transaction": [
{
"event": "transaction.created",
"description": "Transaction has been created",
"category": "transaction"
},
{
"event": "transaction.completed",
"description": "Transaction successfully completed",
"category": "transaction"
}
],
"device": [...],
"branch": [...],
"compliance": [...]
},
"meta": {
"total_events": 15,
"categories": ["transaction", "device", "branch", "compliance"]
}
}Update Webhook
Update webhook configuration.
/api/v1/webhooks/{id}Update Webhook
Update an existing webhook configuration.
id*Webhook ID
Response
Status: 200 OK
{
"status": "success",
"message": "Webhook updated successfully",
"data": {
"id": 1,
"url": "https://new-server.com/webhooks/ctax",
"events": ["transaction.completed"],
"is_active": true,
"updated_at": "2025-01-15T15:00:00Z"
}
}Delete Webhook
Remove a webhook configuration.
/api/v1/webhooks/{id}Delete Webhook
Remove a webhook configuration. This will stop all deliveries to this endpoint.
id*Webhook ID
Response
Status: 200 OK
{
"status": "success",
"message": "Webhook deleted successfully"
}Test Webhook
Send a test payload to verify your webhook endpoint.
/api/v1/webhooks/{id}/testTest Webhook
Send a test payload to verify your webhook endpoint is working correctly.
id*Webhook ID
Response
Status: 200 OK (if successful)
{
"status": "success",
"message": "Test webhook delivered successfully",
"data": {
"webhook_id": 1,
"log_id": 123,
"url": "https://your-server.com/webhooks/ctax",
"response_code": 200,
"delivered_at": "2025-01-15T14:30:00Z"
}
}Status: 422 Unprocessable Entity (if failed)
{
"status": "error",
"message": "Test webhook delivery failed",
"data": {
"webhook_id": 1,
"log_id": 123,
"url": "https://your-server.com/webhooks/ctax",
"response_code": 500
}
}Webhook Logs
List Webhook Logs
Get delivery logs for all webhooks.
/api/v1/webhooks/logsList Webhook Logs
Get delivery logs for webhooks with optional filters.
webhook_idFilter by specific webhook
eventFilter by event type
statusFilter by status (pending, success, failed)
from_dateStart date (YYYY-MM-DD)
to_dateEnd date (YYYY-MM-DD)
per_pageResults per page (max 100, default 50)
Response
Status: 200 OK
{
"data": [
{
"id": 123,
"event": "transaction.completed",
"url": "https://your-server.com/webhooks/ctax",
"status": "success",
"response_code": 200,
"attempts": 1,
"transaction_id": "uuid-here",
"delivered_at": "2025-01-15T14:30:00Z",
"created_at": "2025-01-15T14:30:00Z"
}
],
"meta": {
"current_page": 1,
"per_page": 50,
"total": 156,
"total_pages": 4
}
}Get Webhook Log Details
/api/v1/webhooks/logs/{id}Get Webhook Log Details
Retrieve detailed information about a specific webhook delivery.
id*Log ID
Response
Status: 200 OK
{
"status": "success",
"data": {
"id": 123,
"webhook_config_id": 1,
"transaction_id": "uuid-here",
"event": "transaction.completed",
"url": "https://your-server.com/webhooks/ctax",
"payload": {
"event": "transaction.completed",
"timestamp": "2025-01-15T14:30:00Z",
"data": {...}
},
"status": "success",
"response_code": 200,
"response_body": "{\"received\": true}",
"attempts": 1,
"delivered_at": "2025-01-15T14:30:00Z",
"created_at": "2025-01-15T14:30:00Z",
"updated_at": "2025-01-15T14:30:00Z"
}
}Retry Failed Webhook
/api/v1/webhooks/logs/{id}/retryRetry Failed Webhook
Retry a failed webhook delivery.
id*Log ID
Response
Status: 200 OK (if successful)
{
"status": "success",
"message": "Webhook retry successful",
"data": {
"id": 123,
"status": "success",
"attempts": 2
}
}Webhook Payload Format
All webhooks are sent as HTTP POST requests with JSON body:
Headers
Content-Type: application/json
X-CTax-Webhook-ID: 1
X-CTax-Event: transaction.completed
X-CTax-Timestamp: 1705329000
X-CTax-Signature: sha256=abc123...Payload Structure
{
"event": "transaction.completed",
"timestamp": "2025-01-15T14:30:00Z",
"webhook_id": 1,
"data": {
"transaction_id": "9d3f4b1a-7c8e-4d2f-b5a6-1e3c9f8d2b4a",
"type": "sales",
"status": "success",
"invoice_no": "INV-2025-001",
"receipt_no": "20250115-00-00001",
"branch_id": "00",
"total_amount": 23600,
"tax_amount": 3600,
"synced_to_kra": true,
"processed_at": "2025-01-15T14:30:00Z"
}
}Signature Verification
All webhook payloads are signed using HMAC-SHA256. Verify the signature to ensure the webhook is from C-Tax.
Verification Process
- Extract the signature from the
X-CTax-Signatureheader - Compute HMAC-SHA256 of the raw request body using your webhook secret
- Compare the computed signature with the received signature
Code Examples
PHP
function verifyWebhookSignature($payload, $signature, $secret) {
$computedSignature = 'sha256=' . hash_hmac('sha256', $payload, $secret);
return hash_equals($computedSignature, $signature);
}
// In your webhook handler
$payload = file_get_contents('php://input');
$signature = $_SERVER['HTTP_X_CTAX_SIGNATURE'] ?? '';
$secret = 'whsec_your_secret';
if (!verifyWebhookSignature($payload, $signature, $secret)) {
http_response_code(401);
die('Invalid signature');
}
$event = json_decode($payload, true);
// Process the event...JavaScript (Node.js)
const crypto = require('crypto');
function verifyWebhookSignature(payload, signature, secret) {
const computedSignature = 'sha256=' +
crypto.createHmac('sha256', secret)
.update(payload)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(computedSignature),
Buffer.from(signature)
);
}
// Express.js middleware
app.post('/webhooks/ctax', express.raw({ type: 'application/json' }), (req, res) => {
const signature = req.headers['x-ctax-signature'];
const secret = process.env.CTAX_WEBHOOK_SECRET;
if (!verifyWebhookSignature(req.body, signature, secret)) {
return res.status(401).send('Invalid signature');
}
const event = JSON.parse(req.body);
// Process the event...
res.status(200).send('OK');
});Python
import hmac
import hashlib
def verify_webhook_signature(payload, signature, secret):
computed_signature = 'sha256=' + hmac.new(
secret.encode(),
payload.encode(),
hashlib.sha256
).hexdigest()
return hmac.compare_digest(computed_signature, signature)
# Flask example
@app.route('/webhooks/ctax', methods=['POST'])
def webhook_handler():
payload = request.get_data(as_text=True)
signature = request.headers.get('X-CTax-Signature', '')
secret = os.environ.get('CTAX_WEBHOOK_SECRET')
if not verify_webhook_signature(payload, signature, secret):
return 'Invalid signature', 401
event = request.get_json()
# Process the event...
return 'OK', 200Retry Behavior
Failed webhook deliveries are automatically retried with exponential backoff:
| Attempt | Delay |
|---|---|
| 1 | Immediate |
| 2 | 1 minute |
| 3 | 5 minutes |
| 4 | 30 minutes |
| 5 | 2 hours |
After 5 failed attempts, the webhook is marked as failed. You can manually retry using the API.
Webhook Requirements
Your webhook endpoint should:
- Respond quickly - Return 2xx status within 10 seconds
- Be idempotent - Handle duplicate deliveries gracefully
- Use HTTPS - Required for production (HTTP allowed for testing)
- Return 2xx - Any 2xx status is considered successful
Best Practices
- Verify Signatures - Always verify webhook signatures in production
- Respond Quickly - Process webhooks asynchronously if needed
- Handle Duplicates - Use transaction_id for idempotency
- Log Everything - Keep logs for debugging
- Monitor Failures - Set up alerts for failed webhooks
- Use HTTPS - Encrypt webhook traffic
- Rotate Secrets - Periodically rotate webhook secrets
Error Handling
Common Errors
401 Unauthorized
{
"status": "error",
"message": "Webhook configuration not found"
}400 Bad Request
{
"status": "error",
"message": "Webhook was already successfully delivered"
}422 Unprocessable Entity
{
"status": "error",
"message": "Validation failed",
"errors": {
"url": ["The url must be a valid URL."],
"events": ["The events field is required."]
}
}Related Endpoints
- Transactions API - Transaction details
- Sales API - Sales transactions
- Devices API - Device management