TIB Financial Solutions
Version 1.0.0 | October 3, 2025
TIB webhooks allow your system to receive real-time notifications about transaction events (payments, deposits, status changes, etc.).
200 OKCreate a public URL that will receive webhooks:
Example: https://your-domain.com/api/webhooks/tib/transaction
Recommendations:
Contact your TIB representative and provide the complete URL of your endpoint.
204.225.113.0/24
This configuration must be performed on your network infrastructure (firewall, security groups, WAF, reverse proxy, etc.) according to your tools and policies.
Without this filtering, anyone on the Internet could send fake requests to your endpoint and potentially manipulate your financial data.
| Layer | Description | Priority |
|---|---|---|
| IP Filtering | Allow only 204.225.113.0/24 | MANDATORY |
| HTTPS | Data encryption in transit | Strongly Recommended |
| Payload Validation | Verify JSON structure | Recommended |
| Logging | Record all received webhooks | Recommended |
POST /api/webhooks/tib/transaction HTTP/1.1
Host: your-domain.com
Content-Type: application/json
{
"EventName": "TransactionEvent",
"Target": "Client",
"Direction": "Collect",
...
}
| Property | Type | Description | Possible Values |
|---|---|---|---|
EventName |
string | Event name | "TransactionEvent" |
Target |
string | Transaction target | "Client" / "Merchant" |
Direction |
string | Financial flow direction | "Collect" / "Deposit" |
Step |
string | Process step | "Validation" / "Transmission" / "StatusCheck" / "Payback" |
Status |
string | Event status | See Status section |
DescriptionCode |
string | Description code | "0" to "7" |
Description |
string | Internal technical description | For debugging |
Amount |
string | Formatted amount | Ex: "123.45 $" |
MerchantId |
string (GUID) | Unique merchant identifier | |
MerchantName |
string | Merchant name | |
MerchantAccountPreview |
string | Merchant account preview | Ex: "***-*****-**1111" |
MerchantExternalId |
string | External merchant ID | |
MerchantDescription |
string | Merchant description | |
TransferPayload |
array | List of transfers | See TransferPayload |
| Property | Type | Description |
|---|---|---|
TransferId |
string (GUID) | Unique transfer identifier |
Amount |
decimal | Transfer amount |
OperationKind |
int | Operation type (see OperationKind enum) |
OriginalAmount |
decimal | Original amount |
ExternalReferenceId |
string | External reference defined by you |
DueDate |
string | Due date - Format: "MM/DD/YYYY HH:MM:SS AM/PM" |
CreatedDate |
string | Creation date |
PaymentMethodId |
string (GUID) | Payment method ID |
PaymentMethodDescription |
string | Method description |
PaymentMethodPreview |
string | Masked preview |
CustomerId |
string (GUID) | Customer ID |
CustomerName |
string | Customer name |
CustomerExternalId |
string | Your external customer ID |
BillAmount |
string | Bill amount |
BillCurrency |
string | "1" (CAD) / "2" (USD) / "3" (EUR) / "4" (GBP) |
BillDescription |
string | Bill description |
BillId |
string (GUID) | Bill ID |
BillLanguage |
string | "1" (French) / "2" (English) |
BillTitle |
string | Bill title |
BillCreatedDate |
string | Bill creation date |
ExternalSystemBillNumber1 |
string | External system bill number 1 |
ExternalSystemBillNumber2 |
string | External system bill number 2 |
ExternalSystemBillNumber3 |
string | External system bill number 3 |
BillMerchantId |
string (GUID) | Merchant ID on bill |
BillCustomerId |
string (GUID) | Customer ID on bill |
Payout |
object | Payout information (US only) |
| Status | Meaning | Recommended Action |
|---|---|---|
Success_Success |
Transaction completed successfully | Mark as paid |
Success_NoResultReturned |
Success but result pending | Monitor next webhooks |
Success_Skip |
Step skipped | Informational |
Success_WaitManual |
Waiting for manual intervention | Wait for TIB validation |
Success_Error |
Partial success with error | Check details |
Error_Temporary |
Temporary error | Will be retried automatically |
Error_Fatal |
Fatal error | Transaction failed permanently |
Abort |
Transaction cancelled | Transaction abandoned |
| Code | Name | Description |
|---|---|---|
| "0" | NotSet | Not defined |
| "1" | Confirmed | Transaction confirmed |
| "2" | ErrorOther | Other/generic error |
| "3" | NoFund | Insufficient funds |
| "4" | AccountError | Bank account error |
| "5" | Opposition | Bank opposition |
| "6" | InteracRefused | Interac transaction refused |
| "7" | InteracFailed | Interac transaction failed |
| Value | Name | Description |
|---|---|---|
| -1 | Unknown | Unknown type |
| 0 | NotSet | Not defined |
| 1 | Payment | Standard payment |
| 2 | FreeDeposit | Free deposit |
| 3 | FreeCollection | Free collection |
| 4 | FeePayment | Payment fee |
| 5 | FeeFreeDeposit | Free deposit fee |
| 6 | FeeFreeCollection | Free collection fee |
| 10 | ReturnCharge | Return charge |
| 11 | FileFees | File fees |
| 12 | Refund | Refund |
| 13 | TibCollect | TIB collection |
| 14 | AccountCheckNsfFees | NSF fees account check |
| 15 | AccountCheckOppositionFees | Opposition fees account check |
| 16 | AccountCheckNsfFeesHit | NSF fees hit |
| 17 | AccountCheckOppositionFeesHit | Opposition fees hit |
| 18 | FeeReverseCharge | Reverse charge fee |
| 19 | RevertedOperation | Reverted operation |
| 20 | ConvenientFee | Convenience fee |
| 21 | RevertConvenientFee | Revert convenience fee |
| 22 | PlatformFee | Platform fee |
| 23 | GatewayUsageFee | Gateway usage fee |
| 24 | SupportFee | Support fee |
| 100 | GroupedFees | Grouped fees |
| 101 | FeesRetransfer | Fees retransfer |
1. Validation → Step: Validation, Status: Success_Skip
2. Transmission → Step: Transmission, Status: Success_Success
3. StatusCheck → Step: StatusCheck, Status: Success_NoResultReturned
1. Validation → Status: Success_Skip
2. Transmission → Status: Success_Success
3. StatusCheck → Status: Success_NoResultReturned
4. Payback → Status: Success_Error, DescriptionCode: "3" (NoFund)
1. Validation → Status: Success_Skip
2. Transmission → Status: Success_Success
3. StatusCheck → Status: Success_Error, DescriptionCode: "5" (Opposition)
1. Validation → Status: Success_Skip
2. Transmission → Status: Success_Success
3. Payback → Status: Success_Error, DescriptionCode: "4" (AccountError)
After a successful transaction, TIB validates the payment during these periods. If an issue occurs (NSF, opposition, closed account), you will receive a Payback webhook event.
Internet
↓
Firewall (Allow 204.225.113.0/24)
↓
Endpoint /webhooks
↓
Validate Payload + Log
↓
Return 200 OK (IMPORTANT)
↓
Asynchronous Processing
Your endpoint MUST return HTTP 200 OK in less than 30 seconds.
[HttpPost("webhooks/tib/transaction")]
public IActionResult ReceiveWebhook([FromBody] TransactionEventPayload payload)
{
_ = ProcessInBackground(payload);
return Ok();
}
[HttpPost("webhooks/tib/transaction")]
public IActionResult ReceiveWebhook([FromBody] TransactionEventPayload payload)
{
ProcessTransaction(payload); // Can take too long
return Ok();
}
_logger.LogInformation(
"Webhook received - TransferId: {TransferId}, Status: {Status}, Amount: {Amount}",
transferId,
payload.Status,
payload.Amount
);
try
{
await SaveWebhook(payload);
return Ok();
}
catch (Exception ex)
{
_logger.LogError(ex, "Unable to save webhook");
// Do not return 200 OK
// TIB will know your server is down
return StatusCode(500);
}
If your endpoint does not respond with 200 OK, TIB considers your server is down and temporarily stops sending webhooks.
TIB sends webhook
↓
Your endpoint responds 200 OK → Success, end
↓
No 200 OK (500, timeout, etc.) → Server considered down
| Code | Meaning | TIB Action |
|---|---|---|
| 200 OK | Webhook received and accepted | Success |
| 201, 202, 204 | Accepted (variants) | Success |
| 400 | Invalid request | Server down |
| 401, 403 | Unauthorized | Server down |
| 404 | Endpoint not found | Server down |
| 500, 502, 503 | Server error | Server down |
| Timeout | No response | Server down |
using Microsoft.AspNetCore.Mvc;
using System.Text.Json;
namespace YourApp.Controllers
{
[ApiController]
[Route("api/webhooks/tib")]
public class TibWebhookController : ControllerBase
{
private readonly ILogger<TibWebhookController> _logger;
private readonly ApplicationDbContext _db;
private readonly IBackgroundJobQueue _queue;
public TibWebhookController(
ILogger<TibWebhookController> logger,
ApplicationDbContext db,
IBackgroundJobQueue queue)
{
_logger = logger;
_db = db;
_queue = queue;
}
[HttpPost("transaction")]
public async Task<IActionResult> ReceiveTransactionEvent(
[FromBody] TransactionEventPayload payload)
{
try
{
// 1. Validate payload
if (payload?.TransferPayload == null || !payload.TransferPayload.Any())
{
_logger.LogWarning("Webhook received with invalid payload");
return BadRequest("Invalid payload");
}
var transferId = payload.TransferPayload.First().TransferId;
// 2. Log receipt
_logger.LogInformation(
"Webhook received - Event: {EventName}, TransferId: {TransferId}, " +
"Status: {Status}, Amount: {Amount}",
payload.EventName,
transferId,
payload.Status,
payload.Amount
);
// 3. Save immediately
var webhookEvent = new WebhookEvent
{
Id = Guid.NewGuid(),
TransferId = transferId,
EventName = payload.EventName,
Status = payload.Status,
RawPayload = JsonSerializer.Serialize(payload),
ReceivedAt = DateTime.UtcNow
};
await _db.WebhookEvents.AddAsync(webhookEvent);
await _db.SaveChangesAsync();
// 4. Queue for async processing
_queue.QueueBackgroundWorkItem(async token =>
{
await ProcessWebhookAsync(payload, webhookEvent.Id, token);
});
// 5. IMPORTANT: Return 200 OK immediately
return Ok(new
{
message = "Webhook received successfully",
transferId
});
}
catch (Exception ex)
{
_logger.LogError(ex, "Error receiving webhook");
return StatusCode(500, "Internal error");
}
}
private async Task ProcessWebhookAsync(
TransactionEventPayload payload,
Guid eventId,
CancellationToken cancellationToken)
{
try
{
_logger.LogInformation("Processing started - EventId: {EventId}", eventId);
switch (payload.Status)
{
case "Success_Success":
await ProcessSuccessfulPayment(payload);
break;
case "Error_Fatal":
await ProcessFailedPayment(payload);
break;
default:
_logger.LogInformation("Status: {Status}", payload.Status);
break;
}
_logger.LogInformation("Processing completed - EventId: {EventId}", eventId);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error processing - EventId: {EventId}", eventId);
}
}
private async Task ProcessSuccessfulPayment(TransactionEventPayload payload)
{
var transfer = payload.TransferPayload.First();
_logger.LogInformation(
"Successful payment - Customer: {CustomerName}, Amount: {Amount}",
transfer.CustomerName,
transfer.Amount
);
// Update your system
var billId = transfer.BillId;
var invoice = await _db.Invoices
.FirstOrDefaultAsync(i => i.ExternalBillId == billId);
if (invoice != null)
{
invoice.Status = "Paid";
invoice.PaidAt = DateTime.UtcNow;
invoice.PaidAmount = transfer.Amount ?? 0;
await _db.SaveChangesAsync();
}
}
private async Task ProcessFailedPayment(TransactionEventPayload payload)
{
var transfer = payload.TransferPayload.First();
_logger.LogWarning(
"Failed payment - Customer: {CustomerName}, Code: {DescriptionCode}",
transfer.CustomerName,
payload.DescriptionCode
);
}
}
}
public class WebhookEvent
{
public Guid Id { get; set; }
public string TransferId { get; set; }
public string EventName { get; set; }
public string Status { get; set; }
public string RawPayload { get; set; }
public DateTime ReceivedAt { get; set; }
}
public class TransactionEventPayload
{
public string EventName { get; set; }
public string Target { get; set; }
public string Direction { get; set; }
public string Step { get; set; }
public string Status { get; set; }
public string DescriptionCode { get; set; }
public string Description { get; set; }
public string Amount { get; set; }
public string MerchantId { get; set; }
public string MerchantName { get; set; }
public string MerchantAccountPreview { get; set; }
public string MerchantExternalId { get; set; }
public string MerchantDescription { get; set; }
public List<TransferPayload> TransferPayload { get; set; }
}
public class TransferPayload
{
public string TransferId { get; set; }
public decimal? Amount { get; set; }
public int? OperationKind { get; set; }
public decimal OriginalAmount { get; set; }
public string ExternalReferenceId { get; set; }
public string DueDate { get; set; }
public string CreatedDate { get; set; }
public string PaymentMethodId { get; set; }
public string PaymentMethodDescription { get; set; }
public string PaymentMethodPreview { get; set; }
public string CustomerId { get; set; }
public string CustomerName { get; set; }
public string CustomerExternalId { get; set; }
public string BillAmount { get; set; }
public string BillCurrency { get; set; }
public string BillDescription { get; set; }
public string BillId { get; set; }
public string BillLanguage { get; set; }
public string BillTitle { get; set; }
public string BillCreatedDate { get; set; }
public string ExternalSystemBillNumber1 { get; set; }
public string ExternalSystemBillNumber2 { get; set; }
public string ExternalSystemBillNumber3 { get; set; }
public string BillMerchantId { get; set; }
public string BillCustomerId { get; set; }
public PayoutPayload Payout { get; set; }
}
const express = require('express');
const bodyParser = require('body-parser');
const app = express();
app.use(bodyParser.json());
app.post('/api/webhooks/tib/transaction', async (req, res) => {
try {
const payload = req.body;
// 1. Validate payload
if (!payload.TransferPayload || payload.TransferPayload.length === 0) {
console.warn('Invalid payload received');
return res.status(400).json({ error: 'Invalid payload' });
}
const transferId = payload.TransferPayload[0].TransferId;
// 2. Log
console.log(`Webhook received - TransferId: ${transferId}, Status: ${payload.Status}`);
// 3. Save
await saveWebhook(payload);
// 4. Process asynchronously
processWebhookAsync(payload).catch(err => {
console.error('Async processing error:', err);
});
// 5. IMPORTANT: Respond 200 OK immediately
return res.status(200).json({
message: 'Webhook received successfully',
transferId
});
} catch (error) {
console.error('Critical error:', error);
return res.status(500).json({ error: 'Internal error' });
}
});
async function processWebhookAsync(payload) {
console.log('Async processing started');
switch(payload.Status) {
case 'Success_Success':
await handleSuccessfulPayment(payload);
break;
case 'Error_Fatal':
await handleFailedPayment(payload);
break;
}
console.log('Processing completed');
}
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Webhook server started on port ${PORT}`);
});
from flask import Flask, request, jsonify
import logging
app = Flask(__name__)
logging.basicConfig(level=logging.INFO)
@app.route('/api/webhooks/tib/transaction', methods=['POST'])
def receive_webhook():
try:
payload = request.get_json()
# 1. Validate
if not payload.get('TransferPayload'):
logging.warning('Invalid payload')
return jsonify({'error': 'Invalid payload'}), 400
transfer_id = payload['TransferPayload'][0]['TransferId']
# 2. Log
logging.info(f"Webhook received - TransferId: {transfer_id}, "
f"Status: {payload['Status']}")
# 3. Save
save_webhook(payload)
# 4. Process asynchronously
process_webhook(payload)
# 5. Return 200 OK immediately
return jsonify({
'message': 'Webhook received successfully',
'transferId': transfer_id
}), 200
except Exception as e:
logging.error(f"Critical error: {str(e)}")
return jsonify({'error': 'Internal error'}), 500
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)
Run every scenario below against the TIB sandbox environment before go-live. Webhooks fire for every rail listed on our payment methods page, and our team can wire them into your systems through a business integration.
Request:
POST /api/webhooks/tib/transaction
{
"EventName": "TransactionEvent",
"Target": "Client",
"Direction": "Collect",
"Step": "StatusCheck",
"Status": "Success_Success",
"Amount": "100.00 $",
"TransferPayload": [{
"TransferId": "test-guid-123",
"Amount": 100.00,
"CustomerName": "Test User"
}]
}
Expected result:
Request:
POST /api/webhooks/tib/transaction
{
"EventName": "TransactionEvent"
}
Expected result:
Simulation: Turn off your database
Expected result:
A: TIB will detect that your server does not respond with 200 OK and will temporarily stop sending webhooks. Events during this period will not be delivered.
A: Yes. If your endpoint does not respond with 200 OK, TIB considers your server is down and temporarily stops sending webhooks. When your server responds again with a 200 OK, webhooks will be delivered again.
A: Contact your TIB representative to discuss options for recovering missed events.
A: Contact your TIB representative to discuss this possibility.
A:Webhooks are sent in chronological order of events. However, due to asynchronous processing and network conditions, we cannot guarantee that you will receive them in the same order.
A: Yes, you can receive multiple webhooks for different steps (Validation, Transmission, StatusCheck, etc.).
A: Use the BillCurrency field in TransferPayload to identify the currency.
A: Bank accounts are masked. No complete card numbers are transmitted.
Solutions:
Solutions:
Solutions:
TIB Technical Support:
For effective support, include:
{
"Target": "Client",
"Direction": "Collect",
"Step": "StatusCheck",
"Status": "Success_Success",
"DescriptionCode": "1",
"Description": "Transaction confirmed",
"Amount": "123.45 $",
"MerchantId": "00000000-0000-0000-0000-000000000000",
"MerchantName": "Your Company Inc.",
"MerchantAccountPreview": "***-*****-**1111",
"MerchantExternalId": "MERCHANT-001",
"MerchantDescription": "Service company",
"TransferPayload": [
{
"TransferId": "11111111-1111-1111-1111-111111111111",
"Amount": 123.45,
"OperationKind": 1,
"OriginalAmount": 123.45,
"ExternalReferenceId": "INVOICE-2025-001",
"DueDate": "10/3/2025 12:00:00 PM",
"CreatedDate": "10/1/2025 10:00:00 AM",
"PaymentMethodId": "22222222-2222-2222-2222-222222222222",
"PaymentMethodDescription": "Bank account",
"PaymentMethodPreview": "***-*****-***1111",
"CustomerId": "33333333-3333-3333-3333-333333333333",
"CustomerName": "John Smith",
"CustomerExternalId": "CLIENT-12345",
"BillAmount": "123.45 $",
"BillCurrency": "1",
"BillDescription": "Monthly invoice - October 2025",
"BillId": "44444444-4444-4444-4444-444444444444",
"BillLanguage": "2",
"BillTitle": "Invoice #2025-001",
"BillCreatedDate": "10/1/2025 9:00:00 AM",
"ExternalSystemBillNumber1": "INV-2025-001",
"ExternalSystemBillNumber2": "DEPT-A-001",
"ExternalSystemBillNumber3": "",
"BillMerchantId": "00000000-0000-0000-0000-000000000000",
"BillCustomerId": "33333333-3333-3333-3333-333333333333",
"Payout": null
}
],
"EventName": "TransactionEvent"
}