Documentación de la API de Analysat
Integra tus sistemas con Analysat para automatizar consultas de empresas, cuentas bancarias, transacciones y facturación SAT en tiempo real.
URL base
Todas las solicitudes deben dirigirse a la siguiente URL base:
https://b2b.analysat.com/api_v3/<ENDPOINT>Autenticación
La API usa HTTP Basic Auth. Incluye tus llaves en cada solicitud usando la cabecera Authorization.
Usuario
API_KEY_PUBLICAContraseña
API_KEY_PRIVADAEjemplo con cURL
curl -u <API_KEY_PUBLICA>:<API_KEY_PRIVADA> \
"https://b2b.analysat.com/api_v3/companies/get_companies"Puedes consultar o generar tus llaves en app.analysat.com/integrations/api-keys.
Empresas
/companies/get_companiesObtener empresas
Retorna la lista de todas las empresas (RFCs) asociadas al usuario autenticado. No requiere parámetros adicionales.
Ejemplo cURL
curl -X GET -u <API_KEY_PUBLICA>:<API_KEY_PRIVADA> \
"https://b2b.analysat.com/api_v3/companies/get_companies"Respuesta (200)
[
{
"id": 1234,
"razon_social": "EMPRESA EJEMPLO SA DE CV",
"rfc": "ABC121212ABC",
"vencimiento_ciec": "2025-01-01",
"vencimiento_fiel": "2025-02-10",
"error_304": false,
"fechas_descarga_pendientes": null,
"ultima_actualizacion": "2025-01-05 14:30:45"
}
]Cuentas
/accounts/get_accountsObtener cuentas bancarias
Retorna la lista de cuentas bancarias de una empresa específica identificada por su RFC.
Parámetros de consulta
| Parámetro | Tipo | Requerido | Descripción |
|---|---|---|---|
| rfc | string | Sí | RFC de la empresa cuyos datos se desean consultar. |
Ejemplo cURL
curl -X GET -u <API_KEY_PUBLICA>:<API_KEY_PRIVADA> \
"https://b2b.analysat.com/api_v3/accounts/get_accounts?rfc=ABC121212ABC"Respuesta (200)
[
{
"_id": 714,
"debito_credito": "Debito",
"moneda": "MXN",
"nombre_banco": "BBVA Personal",
"nombre_cuenta": "BBVA",
"numero_cuenta": "154742342",
"tipo_cuenta": "Banco",
"tipo_persona": "Personal",
"saldo_actual": 224848.44
},
{
"_id": 753,
"debito_credito": "Crédito",
"moneda": "MXN",
"nombre_banco": "American Express",
"nombre_cuenta": "The Gold Card",
"numero_cuenta": "XXX-71001",
"tipo_cuenta": "Tarjeta de crédito",
"tipo_persona": "Personal",
"saldo_actual": -11330.99
}
]Transacciones
/transactions/get_transactionsObtener transacciones
Retorna transacciones bancarias paginadas de una empresa. Soporta múltiples filtros por fecha, tipo, monto y estado de conciliación.
Parámetros de consulta
| Parámetro | Tipo | Requerido | Descripción |
|---|---|---|---|
| rfc | string | Sí | RFC de la empresa. |
| fecha_inicial | string | Sí | Fecha de inicio en formato YYYY-MM-DD. |
| fecha_final | string | Sí | Fecha de fin en formato YYYY-MM-DD. |
| pagina | int | No | Número de página. Por defecto: 1. |
| tipo_pago | string | No | "Abono" o "Cargo". |
| excel | string | No | "si" para descargar como Excel (movimientos.xlsx). |
| include_tax_details | string | No | "si" para agregar campos de IVA y retenciones calculados. |
| forma_pago | string | No | Ej: "Transferencia electrónica de fondos". |
| moneda | string | No | Ej: "MXN". |
| id_banco | int | No | ID de la cuenta bancaria. |
| pago_id | int | No | ID de una transacción específica. |
| status_conciliacion | string | No | "Conciliados" o "No conciliados". |
| identificado | string | No | "Todos", "Identificados" o "No identificados". |
| monto_inicial | number | No | Monto mínimo de la transacción. |
| monto_final | number | No | Monto máximo de la transacción. |
| descripcion | string | No | Filtro por texto en la descripción. |
| cliente_proveedor_id | int | No | ID del cliente o proveedor. |
| etiquetas_pagos | string | No | Etiquetas separadas por comas. |
| filtro_etiqueta | string | No | "Se encuentra en" o "No se encuentra en". |
| order_by | string | No | Campo por el que ordenar los resultados. |
| order_by_type | string | No | "asc" o "desc". |
Ejemplo cURL
curl -X GET -u <API_KEY_PUBLICA>:<API_KEY_PRIVADA> \
"https://b2b.analysat.com/api_v3/transactions/get_transactions?rfc=ABC121212ABC&fecha_inicial=2024-01-01&fecha_final=2024-01-31"Respuesta (200)
{
"mas_paginas": false,
"time_elapsed": 0.475,
"resultados": [
{
"_id": 1212,
"fecha": "2024-01-15T06:00:00",
"tipo_pago": "Abono",
"monto_total": 5800.00,
"moneda": "MXN",
"forma_pago": "Transferencia electrónica de fondos",
"nombre_cuenta": "BBVA Empresa",
"nombre_banco": "BBVA",
"descripcion": "SPEI RECIBIDO",
"cliente_proveedor": "PROVEEDOR EJEMPLO SA",
"movimiento_identificado": true,
"documentos_relacionados": [],
"etiquetas": null
}
]
}/transactions/reconciliate_paymentConciliar pago
Asocia una transacción bancaria con una o más facturas (documentos relacionados).
Cuerpo (JSON)
| Parámetro | Tipo | Requerido | Descripción |
|---|---|---|---|
| rfc | string | Sí | RFC de la empresa. |
| pago_id | int | Sí | ID de la transacción bancaria a conciliar. |
| documentos_relacionados | array | Sí | Lista de objetos { "id_factura": "UUID_FACTURAS EMITIDAS", "monto": 1000.00 }. |
| tipo_cambio | number | No | Tipo de cambio. Por defecto: 1. |
| conciliacion_de | string | No | Referencia descriptiva de la conciliación. |
| cliente_proveedor_id | int | No | ID del cliente o proveedor a asociar. |
Ejemplo cURL
curl -X POST -u <API_KEY_PUBLICA>:<API_KEY_PRIVADA> \
-H "Content-Type: application/json" \
-d '{
"rfc": "ABC121212ABC",
"pago_id": 1212,
"documentos_relacionados": [
{ "id_factura": "UUID-1_FACTURAS EMITIDAS", "monto": 5800.00 }
]
}' \
"https://b2b.analysat.com/api_v3/transactions/reconciliate_payment"Respuesta (200)
{ "ok": true, "message": "Pago conciliado correctamente" }/transactions/unreconciliate_paymentDesconciliar pago
Elimina la conciliación entre una transacción y sus documentos relacionados. No es posible desconciliar pagos con complementos de pago vigentes.
Cuerpo (JSON)
| Parámetro | Tipo | Requerido | Descripción |
|---|---|---|---|
| rfc | string | Sí | RFC de la empresa. |
| pago_id | int | Sí | ID de la transacción. |
| doc_rel_pago_ids | array | No | Lista de IDs de doc_rel_pagos a eliminar. Requerido si no se envía id_factura. |
| id_factura | string | No | ID compuesto de factura (ej: "UUID_FACTURAS EMITIDAS"). Requerido si no se envía doc_rel_pago_ids. |
Respuesta (200)
{ "ok": true, "message": "Conciliación eliminada", "deleted_ids": [42, 43] }/transactions/update_paymentActualizar pago
Modifica los metadatos de una transacción bancaria existente. La suma de documentos relacionados no puede superar el monto total.
Cuerpo (JSON)
| Parámetro | Tipo | Requerido | Descripción |
|---|---|---|---|
| rfc | string | Sí | RFC de la empresa. |
| pago_id | int | Sí | ID de la transacción a actualizar. |
| fecha_pago | string | No | Nueva fecha del pago (YYYY-MM-DD). |
| forma_pago | string | No | Nueva forma de pago. |
| descripcion | string | No | Nueva descripción. |
| referencia | string | No | Nueva referencia. |
| monto_total | number | No | Nuevo monto total. |
| tipo_cambio | number | No | Nuevo tipo de cambio. |
| cliente_proveedor_id | int | No | ID del cliente o proveedor a asociar. |
| ids_eliminados | array | No | IDs de doc_rel_pagos a eliminar en esta actualización. |
Respuesta (200)
{ "ok": true, "message": "Pago actualizado correctamente", "pago": { "_id": 1212, "..." } }/transactions/stamp_payment_complementTimbrar complemento de pago
Genera y timbra ante el SAT el CFDI Complemento de Pago para uno o más pagos previamente conciliados. Requiere folios fiscales configurados en la empresa y timbres/crédito disponibles en la cuenta.
Cuerpo (JSON)
| Parámetro | Tipo | Requerido | Descripción |
|---|---|---|---|
| rfc | string | Sí | RFC de la empresa. |
| pago_ids | array | Sí | Lista de IDs de pagos a timbrar. También se acepta un único entero, que será envuelto en una lista. |
| emails | array | No | Lista de correos a los que se enviará el complemento timbrado. |
Ejemplo cURL
curl -X POST -u <API_KEY_PUBLICA>:<API_KEY_PRIVADA> \
-H "Content-Type: application/json" \
-d '{
"rfc": "ABC121212ABC",
"pago_ids": [1212, 1213],
"emails": ["[email protected]"]
}' \
"https://b2b.analysat.com/api_v3/transactions/stamp_payment_complement"Respuesta (200)
{ "ok": true, "message": "Complemento de pago timbrado correctamente", "pago_ids": [1212, 1213] }Facturas
/invoices/get_invoicesObtener facturas
Retorna facturas emitidas o recibidas con paginación. Soporta filtros por fecha, tipo de comprobante, status, conciliación y más.
Parámetros de consulta
| Parámetro | Tipo | Requerido | Descripción |
|---|---|---|---|
| rfc | string | Sí | RFC del contribuyente. |
| fechainicial | string | Sí | Fecha inicial (YYYY-MM-DD). |
| fechafinal | string | Sí | Fecha final (YYYY-MM-DD). |
| emitidas_recibidas | string | Sí | "FACTURAS EMITIDAS" o "FACTURAS RECIBIDAS". |
| tipo_de_comprobante | string | Sí | "I" (Ingreso), "E" (Egreso), "P" (Pago), "N" (Nómina) o "T" (Traslado). |
| status | string | Sí | "Vigente" o "Cancelado". |
| pagina | int | No | Número de página. Por defecto: 1. |
| excel | string | No | "si" para exportar a Excel (facturas.xlsx). |
| q | string | No | Búsqueda de texto libre (RFC, nombre, folio, UUID). |
| status_aprobacion | string | No | "Aprobadas", "No aprobadas" o "Todas" (solo FACTURAS RECIBIDAS tipo I). |
| status_conciliacion | string | No | "Conciliadas" o "No conciliadas". |
| cliente_proveedor_id | int | No | ID del cliente o proveedor. |
| default_vencimiento | int | No | Días de vencimiento por defecto para clientes sin configuración. |
Ejemplo cURL
curl -X GET -u <API_KEY_PUBLICA>:<API_KEY_PRIVADA> \
"https://b2b.analysat.com/api_v3/invoices/get_invoices?rfc=ABC121212ABC&fechainicial=2024-01-01&fechafinal=2024-01-31&emitidas_recibidas=FACTURAS%20EMITIDAS&tipo_de_comprobante=I&status=Vigente"Respuesta (200)
{
"mas_paginas": false,
"time_elapsed": 0.558,
"resultados": [
{
"_id": "UUID-FACTURA_FACTURAS EMITIDAS",
"fecha_convertida": "2024-01-12T14:00:59",
"fecha_vencimiento": "2024-01-22T14:00:59",
"rfc": "CLIENTE-RFC-123",
"nombre": "CLIENTE EJEMPLO SA DE CV",
"folio": "167",
"moneda": "MXN",
"metodo_pago": "PUE",
"tipo_comprobante": "I",
"total": 8120.00,
"subtotal": 7000.00,
"impuestos_trasladados": 1120.00,
"impuestos_retenidos": null,
"total_pagado": null,
"por_pagar_cobrar": 8120.00,
"etiquetas": null,
"aprobada_en": null
}
]
}/invoices/download_invoiceDescargar factura (PDF / XML)
Retorna el archivo PDF o XML de una factura específica.
Parámetros de consulta
| Parámetro | Tipo | Requerido | Descripción |
|---|---|---|---|
| rfc | string | Sí | RFC de la empresa. |
| id_factura | string | Sí | ID compuesto de la factura (ej: "UUID_FACTURAS EMITIDAS"). |
| formato | string | No | "pdf" o "xml". Por defecto: "pdf". |
Ejemplo cURL
curl -X GET -u <API_KEY_PUBLICA>:<API_KEY_PRIVADA> \
"https://b2b.analysat.com/api_v3/invoices/download_invoice?rfc=ABC121212ABC&id_factura=UUID_FACTURAS%20EMITIDAS&formato=pdf" \
--output factura.pdf/invoices/create_invoiceCrear factura
Timbra un CFDI enviándolo directamente al SAT vía Facturapi. Retorna el UUID, fecha de creación y URLs de PDF y XML.
Cuerpo — haz clic en los campos azules para expandir
| Parámetro | Tipo | Requerido | Descripción |
|---|---|---|---|
rfc | string | Sí | RFC del emisor (la empresa en AnalySAT). |
request_ | object | Sí | Datos del CFDI a timbrar. |
emitidas_recibidas | string | No | "FACTURAS EMITIDAS" (defecto) o "FACTURAS RECIBIDAS". |
factura_timbrada | boolean | No | true timbra ante el SAT (defecto). false genera recibo sin timbrar. |
id_cliente | int | No | ID del cliente. Requerido para I/E si no incluyes customer en request_. |
id_empleado | int | No | ID del empleado. Requerido para tipo N si no incluyes customer en request_. |
codigo_postal | string | No | CP del lugar de expedición. Sobreescribe el del cliente si se envía. |
id_almacen | int | No | ID del almacén para movimiento de inventario automático. |
cobrar_factura | boolean | No | true para cobrar vía Stripe Connect tras timbrar. |
payment_method_id | string | No | ID del método de pago Stripe. Requerido si cobrar_factura = true. |
Ejemplo cURL — Factura tipo I
curl -X POST -u <API_KEY_PUBLICA>:<API_KEY_PRIVADA> \
-H "Content-Type: application/json" \
-d '{
"rfc": "ABC121212ABC",
"id_cliente": 42,
"factura_timbrada": true,
"request_": {
"type": "I",
"payment_form": "03",
"payment_method": "PUE",
"use": "G03",
"currency": "MXN",
"series": "A",
"items": [
{
"quantity": 2,
"discount": 0,
"product": {
"description": "Servicio de consultoria tecnologica",
"product_key": "81161500",
"unit_key": "E48",
"price": 5000.00,
"tax_included": false,
"sku": "CONS-001",
"taxes": [{ "type": "IVA", "rate": 0.16, "factor": "Tasa", "withholding": false }]
}
}
]
}
}' \
"https://b2b.analysat.com/api_v3/invoices/create_invoice"Respuesta (201)
{
"id_factura": "FDA12345-ABCD-1234-EFGH-1234567890AB_FACTURAS EMITIDAS",
"uuid": "FDA12345-ABCD-1234-EFGH-1234567890AB",
"fecha_creacion": "2024-01-15T10:30:00",
"message": "Invoice created successfully",
"pdf_url": "https://...",
"xml_url": "https://..."
}/invoices/create_preinvoiceCrear pre-factura (recibo)
Genera un CFDI de prueba usando el entorno sandbox de Facturapi (no se timbra ante el SAT). Útil para validar el layout antes del timbrado real.
create_invoice. Consulta las tablas de estructura de request_ en el endpoint anterior.Cuerpo (JSON) — nivel superior
| Parámetro | Tipo | Requerido | Descripción |
|---|---|---|---|
| rfc | string | Sí | RFC del emisor. |
| request_ | object | Sí | Datos del CFDI. Mismo esquema que create_invoice. |
| id_cliente | int | No | ID del cliente. Requerido para tipo "I" o "E" si no incluyes customer en request_. |
| id_empleado | int | No | ID del empleado. Requerido para tipo "N" si no incluyes customer en request_. |
| codigo_postal | string | No | Código postal del lugar de expedición. |
Respuesta (201)
{
"message": "Pre-factura creada correctamente",
"fecha_creacion": "2024-01-15T10:30:00",
"pdf": "https://...",
"xml": "https://..."
}/invoices/cancel_invoiceCancelar factura
Cancela una o varias facturas con el motivo SAT correspondiente. Las cancelaciones en lote son asíncronas y retornan HTTP 202.
Cuerpo (JSON)
| Parámetro | Tipo | Requerido | Descripción |
|---|---|---|---|
| rfc | string | Sí | RFC de la empresa emisora. |
| id_factura | string | array | Sí | ID de la factura o lista de IDs a cancelar. |
| motive | string | Sí | Motivo SAT: "01" (sustitución), "02", "03" o "04". |
| substitution | string | No | UUID de la factura sustituta. Requerido cuando motive = "01". |
Respuestas
// Cancelación individual (200)
{ "ok": true, "message": "Factura cancelada", "id_factura": "UUID_FACTURAS EMITIDAS" }
// Cancelación en lote — procesamiento asíncrono (202)
{ "ok": true, "message": "Solicitud de cancelación en proceso" }/invoices/download_cancellation_receiptDescargar acuse de cancelación
Descarga el acuse de cancelación de una factura en formato PDF o XML.
Parámetros de consulta
| Parámetro | Tipo | Requerido | Descripción |
|---|---|---|---|
| rfc | string | Sí | RFC de la empresa. |
| id_factura | string | Sí | ID compuesto de la factura cancelada. |
| formato | string | No | "pdf" o "xml". Por defecto: "pdf". |
Ejemplo cURL
curl -X GET -u <API_KEY_PUBLICA>:<API_KEY_PRIVADA> \
"https://b2b.analysat.com/api_v3/invoices/download_cancellation_receipt?rfc=ABC121212ABC&id_factura=UUID_FACTURAS%20EMITIDAS&formato=xml" \
--output acuse.xml/invoices/replace_receipt_with_invoiceReemplazar recibo con factura
Vincula un recibo no timbrado con una factura timbrada vigente. Actualiza los pagos conciliados y cancela el recibo.
Cuerpo (JSON)
| Parámetro | Tipo | Requerido | Descripción |
|---|---|---|---|
| rfc | string | Sí | RFC de la empresa. |
| id_recibo | string | Sí | ID del recibo a reemplazar (debe ser tipo I, no timbrado y no cancelado). |
| id_factura | string | Sí | ID de la factura timbrada destino (debe ser tipo I, vigente). |
Respuesta (200)
{
"ok": true,
"message": "Recibo reemplazado correctamente",
"pagos_actualizados": 2,
"id_recibo_cancelado": "UUID-RECIBO_FACTURAS EMITIDAS",
"id_factura_destino": "UUID-FACTURA_FACTURAS EMITIDAS"
}Descargas
/downloads/create_downloadCrear solicitud de descarga
Inicia una descarga masiva de facturas XML/PDF del SAT para un periodo determinado. Retorna un id_solicitud para consultar el estado.
-F en cURL.Parámetros (form-data)
| Parámetro | Tipo | Requerido | Descripción |
|---|---|---|---|
| rfc | string | Sí | RFC del contribuyente. |
| fecha_inicial | date | Sí | Fecha inicial (YYYY-MM-DD). |
| fecha_final | date | Sí | Fecha final (YYYY-MM-DD). |
| status | string | No | "Vigentes" o "Canceladas". Por defecto: "Todas". |
| formato | string | No | "XML" o "PDF". Por defecto: "XML". |
| emitidas_recibidas | string | No | "FACTURAS EMITIDAS", "FACTURAS RECIBIDAS" o "FACTURAS EMITIDAS Y RECIBIDAS" (defecto). |
| tipo_comprobante | string | No | "Ingreso", "Egreso", "Pago", "Nomina", "Traslado" o "Todas" (defecto). |
Ejemplo cURL
curl -X POST -u <API_KEY_PUBLICA>:<API_KEY_PRIVADA> \
-F "rfc=ABC121212ABC" \
-F "fecha_inicial=2024-01-01" \
-F "fecha_final=2024-01-31" \
-F "status=Vigentes" \
-F "emitidas_recibidas=FACTURAS RECIBIDAS" \
"https://b2b.analysat.com/api_v3/downloads/create_download"Respuesta (200)
{ "id_solicitud": "123456789" }/downloads/get_downloadObtener descarga
Consulta el estado de una solicitud de descarga. Si está lista, retorna una URL prefirmada de S3 con validez de 60 segundos.
Parámetros de consulta
| Parámetro | Tipo | Requerido | Descripción |
|---|---|---|---|
| id | string | Sí | ID de la solicitud obtenido de create_download. |
| rfc | string | Sí | RFC de la empresa. |
Ejemplo cURL
curl -X GET -u <API_KEY_PUBLICA>:<API_KEY_PRIVADA> \
"https://b2b.analysat.com/api_v3/downloads/get_download?id=123456789&rfc=ABC121212ABC"Respuestas posibles
// Descarga lista (200)
{
"download_url": "https://s3.amazonaws.com/.../facturas.zip?...",
"expiry_seconds": 60,
"id_solicitud": "123456789"
}
// Aún procesando (200)
{ "message": "Esta solicitud se encuentra en proceso de descarga" }
// No encontrada (400)
{ "message": "No se encontro la solicitud" }
// Sin resultados (400)
{ "message": "No se encontraron facturas que coincidan con estos filtros" }Reportes
/reports/download_reportDescargar reporte
Genera y descarga un reporte Excel (.xlsx) pre-calculado. Retorna una URL prefirmada de S3 con validez de 60 segundos.
Parámetros de consulta
| Parámetro | Tipo | Requerido | Descripción |
|---|---|---|---|
| rfc | string | Sí | RFC del contribuyente. |
| report | string | Sí | Nombre del reporte. Usa "Accounting 1" para el reporte contable principal. |
| year | string | Sí | Año del reporte (ej: "2024"). |
| version | string | No | "live" (por defecto) u otra versión del reporte. |
Ejemplo cURL
curl -X GET -u <API_KEY_PUBLICA>:<API_KEY_PRIVADA> \
"https://b2b.analysat.com/api_v3/reports/download_report?rfc=ABC121212ABC&report=Accounting%201&year=2024"Respuesta (200)
{
"download_url": "https://s3.amazonaws.com/.../reporte.xlsx?...",
"expiry_seconds": 60,
"report_name": "Accounting 1"
}Webhooks
Los webhooks permiten que Analysat notifique a tu sistema en tiempo real cuando ocurren cambios en facturas y pagos de tus empresas. En lugar de hacer polling a la API, configura una URL y recibirás las notificaciones de forma automática.
¿Cómo funcionan?
Cuando se produce un evento (por ejemplo, una factura nueva), Analysat agrupa los cambios pendientes y los envía en una sola solicitud POST a la URL de tu endpoint. El sistema reintenta automáticamente con backoff exponencial hasta 8 veces si tu servidor no responde con un código 2xx.
Eventos disponibles
| Evento | Descripción |
|---|---|
| factura.created | Se creó una nueva factura. |
| factura.updated | Se actualizó una factura existente. |
| factura.deleted | Se eliminó o canceló una factura. |
| pago.created | Se registró un nuevo pago (transacción bancaria). |
| pago.updated | Se modificó un pago existente. |
| pago.deleted | Se eliminó un pago. |
Estructura del payload
Cada entrega agrupa uno o más eventos del mismo ciclo de procesamiento. El cuerpo de la solicitud tiene la siguiente forma:
{
"delivery_type": "batch",
"id_empresa": 1234,
"delivery_id": 42,
"event_ids": [101, 102],
"events": [
{
"id": 101,
"type": "factura.created",
"resource_type": "factura",
"resource_id": "5678",
"payload": {
"id": "5678",
"id_empresa": 1234,
"op": "INSERT"
},
"created_at": "2024-01-15T10:30:00",
"compressed_event_ids": [101]
}
]
}Verificación de firma
Cada solicitud incluye las siguientes cabeceras para que puedas verificar su autenticidad:
| Cabecera | Descripción |
|---|---|
| X-Analysat-Webhook-Id | ID numérico de la entrega. |
| X-Analysat-Webhook-Timestamp | Unix timestamp (segundos) del momento del envío. |
| X-Analysat-Webhook-Signature | HMAC-SHA256 con formato sha256=<hex>. Firmado sobre "<timestamp>.<body>". |
Para validar la firma, concatena el timestamp y el cuerpo JSON exacto separados por un punto, calcula el HMAC-SHA256 con tu secreto y compáralo con el valor de la cabecera:
import hmac, hashlib
def verify_signature(secret: str, timestamp: str, body: bytes, signature_header: str) -> bool:
message = f"{timestamp}.{body.decode('utf-8')}".encode("utf-8")
expected = "sha256=" + hmac.new(secret.encode("utf-8"), message, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, signature_header)