Overview
Transaction webhook events allow you to receive real-time notifications when transactions are created or updated in your organization. Gu1 automatically sends HTTP POST requests to your configured webhook endpoint, enabling you to automate transaction monitoring workflows, fraud detection, and regulatory compliance.
Why Use Transaction Webhooks?
Real-Time Monitoring Receive instant notifications about new or updated transactions
Fraud Detection Implement additional security checks in real-time
Workflow Automation Trigger automated processes based on transaction activity
Audit and Compliance Keep audit logs synchronized across all your systems
Available Events
Gu1 sends webhooks for the following transaction events:
Event Type Description When Triggered transaction.createdTransaction created When a new transaction is recorded in the system transaction.updatedTransaction updated When a transaction’s status or information is updated
The transaction.created and transaction.updated events are currently in development and will be activated soon. This documentation is available to prepare your integration.
Event Payload Structure
All transaction webhook events follow this standard structure:
{
"event" : "transaction.created" ,
"timestamp" : "2025-01-29T15:30:00Z" ,
"organizationId" : "org-123" ,
"payload" : {
"transactionId" : "550e8400-e29b-41d4-a716-446655440000" ,
"externalId" : "txn_abc123" ,
"type" : "payment" ,
"status" : "CREATED" ,
"amount" : 5000.00 ,
"currency" : "USD"
// ... event-specific fields
}
}
Common Payload Fields
The event type (e.g., transaction.created)
ISO 8601 timestamp when the event occurred
The transaction’s UUID in Gu1
Your external ID for the transaction
Transaction type: payment, transfer, withdrawal, etc.
Current transaction status: CREATED, PROCESSING, SUSPENDED, SENT, SUCCESSFUL, DECLINED, REFUNDED, EXPIRED
Transaction amount in original currency
ISO 4217 currency code (e.g., USD, EUR, MXN)
Event-Specific Payloads
transaction.created
Sent when a new transaction is recorded in the system.
{
"event" : "transaction.created" ,
"timestamp" : "2025-01-29T15:30:00Z" ,
"organizationId" : "org-123" ,
"payload" : {
"transactionId" : "550e8400-e29b-41d4-a716-446655440000" ,
"externalId" : "txn_abc123" ,
"type" : "payment" ,
"status" : "CREATED" ,
"amount" : 5000.00 ,
"currency" : "USD" ,
"amountInUsd" : 5000.00 ,
"origin" : {
"entityId" : "123e4567-e89b-12d3-a456-426614174001" ,
"externalId" : "customer_john" ,
"name" : "John Doe" ,
"country" : "US"
},
"destination" : {
"entityId" : "123e4567-e89b-12d3-a456-426614174002" ,
"externalId" : "merchant_acme" ,
"name" : "ACME Corp" ,
"country" : "US"
},
"transactedAt" : "2025-01-29T15:30:00Z" ,
"createdAt" : "2025-01-29T15:30:00Z"
}
}
Use case : Trigger additional fraud checks, update account balances in real-time, or initiate compliance processes.
transaction.updated
Sent when an existing transaction is updated (e.g., status change).
{
"event" : "transaction.updated" ,
"timestamp" : "2025-01-29T15:35:00Z" ,
"organizationId" : "org-123" ,
"payload" : {
"transactionId" : "550e8400-e29b-41d4-a716-446655440000" ,
"externalId" : "txn_abc123" ,
"type" : "payment" ,
"status" : "SUCCESSFUL" ,
"amount" : 5000.00 ,
"currency" : "USD" ,
"amountInUsd" : 5000.00 ,
"origin" : {
"entityId" : "123e4567-e89b-12d3-a456-426614174001" ,
"externalId" : "customer_john" ,
"name" : "John Doe" ,
"country" : "US"
},
"destination" : {
"entityId" : "123e4567-e89b-12d3-a456-426614174002" ,
"externalId" : "merchant_acme" ,
"name" : "ACME Corp" ,
"country" : "US"
},
"previousStatus" : "PROCESSING" ,
"newStatus" : "SUCCESSFUL" ,
"updatedAt" : "2025-01-29T15:35:00Z"
}
}
Use case : Notify customers about their transaction status, update dashboards in real-time, or trigger post-transaction workflows.
Code Examples
Node.js - Handling Transaction Events
const express = require ( 'express' );
const crypto = require ( 'crypto' );
const app = express ();
app . use ( express . json ({
verify : ( req , res , buf ) => {
req . rawBody = buf . toString ( 'utf8' );
}
}));
app . post ( '/webhooks/transactions' , async ( req , res ) => {
try {
// Verify webhook signature (see security guide)
const signature = req . headers [ 'x-webhook-signature' ];
const webhookSecret = process . env . GU1_WEBHOOK_SECRET ;
if ( ! verifySignature ( req . rawBody , signature , webhookSecret )) {
console . error ( 'Invalid webhook signature' );
return res . status ( 401 ). json ({ error: 'Invalid signature' });
}
// Extract webhook data
const { event , timestamp , organizationId , payload } = req . body ;
console . log ( 'Received transaction webhook:' , {
event ,
transactionId: payload . transactionId ,
status: payload . status
});
// Process webhook based on event type
await handleTransactionWebhook ( event , payload );
// Return 200 to confirm receipt
res . status ( 200 ). json ({
success: true ,
message: 'Webhook received'
});
} catch ( error ) {
console . error ( 'Webhook error:' , error );
res . status ( 500 ). json ({
error: error . message
});
}
});
async function handleTransactionWebhook ( event , data ) {
const { transactionId , externalId , status , amount , currency } = data ;
// Update your database with Gu1 transaction
await db . updateTransaction ( externalId , {
gu1TransactionId: transactionId ,
status: status ,
lastUpdated: new Date ()
});
// Perform actions based on event type
switch ( event ) {
case 'transaction.created' :
console . log ( 'New transaction created:' , externalId );
// Additional fraud checks
if ( amount > 10000 ) {
await triggerHighValueReview ( transactionId );
}
// Notify customer
await notifyCustomer ( data . origin . externalId , 'transaction-created' , {
amount ,
currency ,
recipient: data . destination . name
});
break ;
case 'transaction.updated' :
console . log ( 'Transaction status changed:' , {
externalId ,
from: data . previousStatus ,
to: data . newStatus
});
// Update status in your system
await db . updateTransaction ( externalId , {
status: data . newStatus ,
statusChangedAt: new Date ()
});
// Notify about completed transaction
if ( data . newStatus === 'SUCCESSFUL' ) {
await notifyCustomer ( data . origin . externalId , 'transaction-completed' , {
amount ,
currency ,
recipient: data . destination . name
});
}
// Handle declined transactions
if ( data . newStatus === 'DECLINED' ) {
await notifyCustomer ( data . origin . externalId , 'transaction-declined' , {
amount ,
currency
});
await logDeclinedTransaction ( transactionId , externalId );
}
break ;
}
}
function verifySignature ( rawBody , signature , secret ) {
const expectedSignature = crypto
. createHmac ( 'sha256' , secret )
. update ( rawBody )
. digest ( 'hex' );
return signature === expectedSignature ;
}
app . listen ( 3000 );
Python - Handling Transaction Events
from flask import Flask, request, jsonify
import hmac
import hashlib
from datetime import datetime
app = Flask( __name__ )
@app.route ( '/webhooks/transactions' , methods = [ 'POST' ])
def handle_transaction_webhook ():
try :
# Verify webhook signature
signature = request.headers.get( 'X-Webhook-Signature' )
webhook_secret = os.getenv( 'GU1_WEBHOOK_SECRET' )
if not verify_signature(request.data, signature, webhook_secret):
return jsonify({ 'error' : 'Invalid signature' }), 401
# Process webhook
data = request.json
event = data[ 'event' ]
payload = data[ 'payload' ]
print ( f 'Received transaction webhook: { event } ' )
# Handle event
handle_transaction_event(event, payload)
return jsonify({
'success' : True ,
'message' : 'Webhook received'
}), 200
except Exception as e:
print ( f 'Webhook error: { str (e) } ' )
return jsonify({ 'error' : str (e)}), 500
def handle_transaction_event ( event , data ):
transaction_id = data[ 'transactionId' ]
external_id = data[ 'externalId' ]
# Update database
db.update_transaction(
external_id = external_id,
gu1_transaction_id = transaction_id,
status = data[ 'status' ],
last_updated = datetime.now()
)
# Handle different events
if event == 'transaction.created' :
# Fraud checks
if data[ 'amount' ] > 10000 :
trigger_high_value_review(transaction_id)
# Notify customer
notify_customer(
data[ 'origin' ][ 'externalId' ],
'transaction-created' ,
data
)
elif event == 'transaction.updated' :
# Log status change
log_status_change(
external_id,
data.get( 'previousStatus' ),
data[ 'status' ]
)
# Notify about completed
if data[ 'status' ] == 'SUCCESSFUL' :
notify_customer(
data[ 'origin' ][ 'externalId' ],
'transaction-completed' ,
data
)
# Handle declined
elif data[ 'status' ] == 'DECLINED' :
notify_customer(
data[ 'origin' ][ 'externalId' ],
'transaction-declined' ,
data
)
def verify_signature ( raw_body , signature , secret ):
expected = hmac.new(
secret.encode( 'utf-8' ),
raw_body,
hashlib.sha256
).hexdigest()
return signature == expected
if __name__ == '__main__' :
app.run( port = 3000 )
Best Practices
Use externalId for Lookup
The webhook includes externalId which is the ID you provided when creating the transaction. Use it to look up the transaction in your database. const transaction = await db . findTransaction ({
externalId: data . externalId
});
Store Gu1 Transaction IDs
Save Gu1’s transactionId in your database. This allows you to query transaction details later if needed. await db . updateTransaction ( transaction . id , {
gu1TransactionId: data . transactionId ,
status: data . status
});
You may receive the same webhook multiple times. Use the transactionId and event to ensure you process each event only once. async function handleWebhook ( webhook ) {
const alreadyProcessed = await db . checkWebhookProcessed (
webhook . payload . transactionId ,
webhook . event
);
if ( alreadyProcessed ) {
return ; // Skip duplicate
}
// Process webhook
await processTransaction ( webhook . payload );
// Mark as processed
await db . markWebhookProcessed (
webhook . payload . transactionId ,
webhook . event
);
}
Always return a 200 status code as quickly as possible to confirm receipt. Process the webhook asynchronously if needed. app . post ( '/webhooks/transactions' , async ( req , res ) => {
// Confirm immediately
res . status ( 200 ). send ( 'OK' );
// Process asynchronously
processWebhook ( req . body ). catch ( console . error );
});
Always verify the X-Webhook-Signature header to ensure the webhook is authentic. See the security guide for details. const signature = req . headers [ 'x-webhook-signature' ];
if ( ! verifySignature ( req . rawBody , signature , secret )) {
return res . status ( 401 ). json ({ error: 'Invalid signature' });
}
Troubleshooting
Check these items:
Webhook URL is publicly accessible via HTTPS
Webhook is configured and enabled in dashboard
Subscribed to correct event types
Endpoint returns 200 status code within 30 seconds
Check server logs for received requests
Transaction events are currently in development - confirm they are enabled for your organization
Signature Verification Failing
Common causes:
Using wrong secret (check dashboard for current secret)
Verifying signature on parsed JSON instead of raw body
Secret not saved correctly after webhook creation
Encoding issues (ensure UTF-8)
See the security guide for proper implementation.
Receiving Duplicate Webhooks
This is normal behavior. Webhooks may be sent multiple times due to network issues, timeouts, or retries. Always implement idempotency using the webhook’s transactionId and event type.
Next Steps