Skip to Content
C-Tax v1.0 is now available

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

EventDescription
transaction.createdTransaction has been created
transaction.processingTransaction is being processed by KRA
transaction.completedTransaction successfully completed
transaction.failedTransaction processing failed
transaction.retryingTransaction is being retried

Device Events

EventDescription
device.registeredNew VSCU device registered
device.initializedVSCU device initialized successfully
device.connection_lostLost connection to VSCU device
device.connection_restoredConnection to VSCU device restored
device.errorVSCU device error occurred

Branch Events

EventDescription
branch.createdNew branch created
branch.updatedBranch information updated
branch.deletedBranch deleted

Compliance Events

EventDescription
compliance.warningCompliance warning issued
compliance.sync_requiredCode data sync required

Create Webhook

Register a new webhook endpoint.

POST/api/v1/webhooks

Create Webhook

Register a new webhook endpoint to receive real-time notifications for specified events.

Authentication
Request BodyJSON

Field Descriptions

FieldTypeRequiredDescription
urlstringYesWebhook endpoint URL (HTTPS recommended)
eventsarrayYesEvents to subscribe to
headersobjectNoCustom 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.

GET/api/v1/webhooks

List Webhooks

Retrieve all webhook configurations for your account.

Authentication

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.

GET/api/v1/webhooks/{id}

Get Webhook Details

Retrieve detailed information about a specific webhook configuration.

Authentication
Parameters
id*
number · path

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.

GET/api/v1/webhooks/events

Get Available Events

List all available webhook events you can subscribe to.

Authentication

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.

PUT/api/v1/webhooks/{id}

Update Webhook

Update an existing webhook configuration.

Authentication
Parameters
id*
number · path

Webhook ID

Request BodyJSON

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.

DELETE/api/v1/webhooks/{id}

Delete Webhook

Remove a webhook configuration. This will stop all deliveries to this endpoint.

Authentication
Parameters
id*
number · path

Webhook ID

Response

Status: 200 OK

{ "status": "success", "message": "Webhook deleted successfully" }

Test Webhook

Send a test payload to verify your webhook endpoint.

POST/api/v1/webhooks/{id}/test

Test Webhook

Send a test payload to verify your webhook endpoint is working correctly.

Authentication
Parameters
id*
number · path

Webhook ID

Request BodyJSON

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.

GET/api/v1/webhooks/logs

List Webhook Logs

Get delivery logs for webhooks with optional filters.

Authentication
Parameters
webhook_id
number · query

Filter by specific webhook

event
string · query

Filter by event type

status
string · query

Filter by status (pending, success, failed)

from_date
string · query

Start date (YYYY-MM-DD)

to_date
string · query

End date (YYYY-MM-DD)

per_page
number · query

Results 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

GET/api/v1/webhooks/logs/{id}

Get Webhook Log Details

Retrieve detailed information about a specific webhook delivery.

Authentication
Parameters
id*
number · path

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

POST/api/v1/webhooks/logs/{id}/retry

Retry Failed Webhook

Retry a failed webhook delivery.

Authentication
Parameters
id*
number · path

Log ID

Request BodyJSON

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

  1. Extract the signature from the X-CTax-Signature header
  2. Compute HMAC-SHA256 of the raw request body using your webhook secret
  3. 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', 200

Retry Behavior

Failed webhook deliveries are automatically retried with exponential backoff:

AttemptDelay
1Immediate
21 minute
35 minutes
430 minutes
52 hours

After 5 failed attempts, the webhook is marked as failed. You can manually retry using the API.


Webhook Requirements

Your webhook endpoint should:

  1. Respond quickly - Return 2xx status within 10 seconds
  2. Be idempotent - Handle duplicate deliveries gracefully
  3. Use HTTPS - Required for production (HTTP allowed for testing)
  4. Return 2xx - Any 2xx status is considered successful

Best Practices

  1. Verify Signatures - Always verify webhook signatures in production
  2. Respond Quickly - Process webhooks asynchronously if needed
  3. Handle Duplicates - Use transaction_id for idempotency
  4. Log Everything - Keep logs for debugging
  5. Monitor Failures - Set up alerts for failed webhooks
  6. Use HTTPS - Encrypt webhook traffic
  7. 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."] } }

Last updated on