Documentation Index Fetch the complete documentation index at: https://docs-mx.taxo.ws/llms.txt
Use this file to discover all available pages before exploring further.
What are webhooks?
Webhooks are HTTP notifications that Taxo sends to your application when important events occur, such as the completion of an extraction. Instead of constant polling, your application receives updates automatically.
Real-time Receive instant notifications when documents are ready
Efficiency Eliminates the need for constant API polling
Reliability Automatic retry system with exponential backoff
Security HMAC-SHA256 signature verification to authenticate events
Webhook configuration
Create endpoint
Click “Add Webhook” and enter your endpoint URL
Select events
Choose the types of events you want to receive:
extraction.completed - Extraction completed successfully
extraction.failed - Extraction failed
document.ready - Individual document ready for download
Configure security
Optionally, configure a secret to verify HMAC signatures
Test connection
Use the “Test Webhook” button to verify that your endpoint responds correctly
Event types
Sent when an extraction completes successfully.
{
"event" : {
"type" : "extraction.completed" ,
"timestamp" : "2025-01-04T13:15:42.123Z" ,
"version" : "1.0"
},
"data" : {
"extractionId" : "JOB20250104123456789A" ,
"status" : "COMPLETED" ,
"completedAt" : "2025-01-04T13:15:42.123Z" ,
"summary" : {
"totalDocuments" : 1500 ,
"successfulDownloads" : 1498 ,
"failedDownloads" : 2 ,
"totalSizeBytes" : 15728640
},
"subject" : {
"identification" : "ABC010101ABC" ,
"fullName" : "Empresa ABC S.A. de C.V." ,
"personType" : "MORAL"
},
"options" : {
"informationType" : "INVOICE" ,
"period" : {
"from" : "2024-01-01" ,
"to" : "2024-12-31"
},
"direction" : "RECEIVED"
}
}
}
Sent when an extraction fails due to an unrecoverable error.
{
"event" : {
"type" : "extraction.failed" ,
"timestamp" : "2025-01-04T12:38:12.456Z" ,
"version" : "1.0"
},
"data" : {
"extractionId" : "JOB20250104123456789A" ,
"status" : "FAILED" ,
"failedAt" : "2025-01-04T12:38:12.456Z" ,
"error" : {
"code" : "INVALID_CREDENTIALS" ,
"message" : "Las credenciales CIEC proporcionadas son incorrectas" ,
"details" : {
"satResponse" : "Usuario o contraseña incorrectos"
}
},
"subject" : {
"identification" : "ABC010101ABC"
},
"options" : {
"informationType" : "INVOICE" ,
"period" : {
"from" : "2024-01-01" ,
"to" : "2024-12-31"
}
}
}
}
document.ready
Sent for each document that is processed successfully (optional, may generate many notifications).
{
"event" : {
"type" : "document.ready" ,
"timestamp" : "2025-01-04T13:10:15.789Z" ,
"version" : "1.0"
},
"data" : {
"extractionId" : "JOB20250104123456789A" ,
"document" : {
"id" : "doc_550e8400-e29b-41d4-a716-446655440000" ,
"uuid" : "12345678-1234-1234-1234-123456789012" ,
"type" : "INVOICE" ,
"issueDate" : "2024-03-15T10:30:00Z" ,
"amount" : 1160.00 ,
"currency" : "MXN" ,
"emitter" : {
"rfc" : "XYZ020202XYZ" ,
"name" : "Proveedor XYZ S.A. de C.V."
},
"receiver" : {
"rfc" : "ABC010101ABC" ,
"name" : "Empresa ABC S.A. de C.V."
},
"availableFormats" : [ "XML" , "PDF" ]
}
}
}
Webhook endpoint implementation
Node.js (Express)
Python (FastAPI)
Java (Spring Boot)
const express = require ( 'express' );
const crypto = require ( 'crypto' );
const app = express ();
// Middleware para capturar el body raw (necesario para verificar firma)
app . use ( '/webhooks/taxo' , express . raw ({ type: 'application/json' }));
app . post ( '/webhooks/taxo' , ( req , res ) => {
const signature = req . headers [ 'x-taxo-signature' ];
const timestamp = req . headers [ 'x-taxo-timestamp' ];
const payload = req . body ;
// Verificar que el webhook no sea muy antiguo (5 minutos)
const webhookTimestamp = parseInt ( timestamp );
const currentTime = Math . floor ( Date . now () / 1000 );
if ( currentTime - webhookTimestamp > 300 ) {
return res . status ( 400 ). json ({ error: 'Webhook too old' });
}
// Verificar firma HMAC (si tienes secreto configurado)
const webhookSecret = process . env . TAXO_WEBHOOK_SECRET ;
if ( webhookSecret && signature ) {
const expectedSignature = crypto
. createHmac ( 'sha256' , webhookSecret )
. update ( timestamp + '.' + payload )
. digest ( 'hex' );
if ( signature !== `sha256= ${ expectedSignature } ` ) {
return res . status ( 401 ). json ({ error: 'Invalid signature' });
}
}
// Parsear el payload
const event = JSON . parse ( payload . toString ());
// Procesar evento
try {
handleWebhookEvent ( event );
// Responder con 200 para confirmar recepción
res . status ( 200 ). json ({ received: true });
} catch ( error ) {
console . error ( 'Error procesando webhook:' , error );
res . status ( 500 ). json ({ error: 'Processing failed' });
}
});
function handleWebhookEvent ( event ) {
console . log ( `Event received: ${ event . event . type } ` );
switch ( event . event . type ) {
case 'extraction.completed' :
handleExtractionCompleted ( event . data );
break ;
case 'extraction.failed' :
handleExtractionFailed ( event . data );
break ;
case 'document.ready' :
handleDocumentReady ( event . data );
break ;
default :
console . log ( `Unhandled event type: ${ event . event . type } ` );
}
}
function handleExtractionCompleted ( data ) {
console . log ( `✅ Extraction ${ data . extractionId } completed:` );
console . log ( ` - ${ data . summary . successfulDownloads } documents downloaded` );
console . log ( ` - ${ data . summary . failedDownloads } failures` );
// Here you can:
// - Update status in your database
// - Send email notification
// - Start automatic document download
// - Process documents with your business logic
}
function handleExtractionFailed ( data ) {
console . error ( `❌ Extraction ${ data . extractionId } failed:` );
console . error ( ` Error: ${ data . error . message } ` );
// Here you can:
// - Mark extraction as failed in your DB
// - Send alert to support team
// - Attempt reprocessing if error is recoverable
}
function handleDocumentReady ( data ) {
console . log ( `📄 Document ready: ${ data . document . uuid } ` );
// Process individual document
// - Download automatically
// - Extract specific data
// - Notify downstream systems
}
app . listen ( 3000 , () => {
console . log ( 'Webhook server running on port 3000' );
});
Signature verification
Webhooks include an HMAC-SHA256 signature to verify their authenticity:
Get data
timestamp: Header X-Taxo-Timestamp
signature: Header X-Taxo-Signature
payload: Body raw del webhook
Build string to sign
Concatenate: timestamp + "." + payload
Calculate HMAC
Use your webhook secret to calculate HMAC-SHA256 of the previous string
Compare signatures
The received signature should equal sha256={your_calculated_hmac}
Best practices
Los webhooks pueden enviarse múltiples veces. Usa el campo event.timestamp como clave de idempotencia para evitar procesamiento duplicado. const processedEvents = new Set ();
function handleWebhookEvent ( event ) {
const eventKey = ` ${ event . event . type } _ ${ event . event . timestamp } ` ;
if ( processedEvents . has ( eventKey )) {
console . log ( 'Evento ya procesado, ignorando' );
return ;
}
// Procesar evento...
processedEvents . add ( eventKey );
}
Configura timeouts apropiados en tu endpoint:
Timeout de respuesta : 10 segundos máximo
Procesamiento asíncrono : Para tareas largas, responde 200 inmediatamente y procesa en background
Reintentos automáticos : Taxo reintentará hasta 5 veces con backoff exponencial
Implementa logging detallado para debugging: function handleWebhookEvent ( event ) {
const correlationId = crypto . randomUUID ();
console . log ( `[ ${ correlationId } ] Webhook recibido:` , {
type: event . event . type ,
timestamp: event . event . timestamp ,
extractionId: event . data . extractionId
});
try {
// Procesar evento...
console . log ( `[ ${ correlationId } ] Evento procesado exitosamente` );
} catch ( error ) {
console . error ( `[ ${ correlationId } ] Error procesando webhook:` , error );
throw error ;
}
}
HTTPS obligatorio : Los webhooks solo se envían a URLs HTTPS
Verificar firma : Siempre valida la firma HMAC en producción
Validar timestamp : Rechaza webhooks muy antiguos (más de 5 minutos)
Rate limiting : Implementa protección contra ataques de denegación de servicio
Testing and debugging
Test webhooks locally
To test webhooks in local development, use tools like ngrok:
# Install ngrok
npm install -g ngrok
# Expose local port
ngrok http 3000
# Use the public URL in webhook configuration
# Ejemplo: https://abc123.ngrok.io/webhooks/taxo
Test webhook
You can test your endpoint with this example payload:
curl -X POST "https://tu-dominio.com/webhooks/taxo" \
-H "Content-Type: application/json" \
-H "X-Taxo-Signature: sha256=test" \
-H "X-Taxo-Timestamp: $( date +%s)" \
-d '{
"event": {
"type": "extraction.completed",
"timestamp": "2025-01-04T13:15:42.123Z",
"version": "1.0"
},
"data": {
"extractionId": "TEST123456789",
"status": "COMPLETED",
"summary": {
"totalDocuments": 100,
"successfulDownloads": 98,
"failedDownloads": 2
}
}
}'
Troubleshooting
Verifica que la URL esté configurada correctamente
Asegúrate de que tu endpoint responda con status 2xx
Revisa que no haya firewalls bloqueando las IPs de Taxo
Confirma que el certificado SSL sea válido
Verifica que el secreto webhook esté configurado correctamente
Asegúrate de usar el body raw (no parseado) para calcular la firma
Confirma que estás concatenando timestamp + ”.” + payload
Verifica que el algoritmo sea HMAC-SHA256
Implementa idempotencia usando el timestamp del evento
Almacena IDs de eventos procesados en cache/base de datos
Responde siempre con 200 even si el evento ya fue procesado