Saltar al contenido principal

Webhooks

Su cuenta tiene una sola URL de webhook y todos los eventos se envían a ella. Configúrela en Dashboard → API o mediante Configurar webhook.

Eventos

Hoy existen seis eventos.
order.updated se envía únicamente para pedidos creados con una clave API. Las compras hechas en el sitio web o mediante el bot de Telegram no generan webhook, aunque la misma cuenta sí reciba con normalidad los eventos de Smart Mode.

Sobre del mensaje

Cada entrega tiene las mismas tres claves de nivel superior. timestamp es el momento en que compusimos el evento, ISO-8601 en UTC.
Response
Cabeceras:

order.updated

number
ID del pedido.
string
energy o bandwidth.
string
processing, filled, partial, failed, reclaimed o reclaim_failed. Este campo es la fuente de verdad; vea la advertencia siguiente.
string
Dirección a la que se delegó el recurso.
number
Cantidad pedida: unidades de energía o puntos de bandwidth.
number
Igual que volume. Está presente para poder comparar lo esperado con lo entregado sin averiguar qué significa cada campo.
number
Cuánto se ha delegado realmente hasta ahora. Igual a volume cuando el pedido está filled; en processing y partial es la suma de las partes ya entregadas, y 0 si falló.
string
Precio del pedido en TRX, como cadena decimal.
string | null
Transacción que delegó el recurso.
string | null
Transacción que lo recuperó al terminar el alquiler.
string | null
Cuándo está previsto recuperar el recurso, ISO-8601, o null en los pedidos que no se recuperan por temporizador.
Response
Juzgue el resultado por status, nunca por la presencia de delegate_txid. Un pedido fallido también puede llevar un identificador de transacción: lo conservamos a propósito para poder investigar el fallo. Solo status dice si recibió el recurso.
Internamente dividimos los pedidos grandes en partes, pero eso es asunto nuestro: usted recibe un webhook por pedido, nunca uno por parte.

subscription.transfer

number
Suscripción de Smart Mode a la que pertenece la transferencia.
string
Dirección vigilada desde la que salió la transferencia.
string | null
La etiqueta que puso a la suscripción.
string
La transacción de la transferencia en la cadena.
number
Cuántas transferencias cubrió esta delegación.
number
Energía delegada para ellas.
string
Lo cobrado por la energía, en TRX.
string | null
Bandwidth cobrado adicionalmente, o null si no se cobró.
string
Saldo de su cuenta tras el cobro.
string
Cuándo se procesó la transferencia. Observe que está dentro de data y es distinto del timestamp del propio sobre.
Response

Eventos de Transfer Package

Los cuatro eventos package.* se envían a la cuenta propietaria del paquete. Un paquete comprado por factura sin iniciar sesión ni usar una clave API no tiene propietario y no genera eventos. Tampoco los generan las facturas canceladas, las compras rechazadas ni las compras en revisión manual; un paquete en revisión envía package.activated cuando se activa. Cada evento package.* incluye estos campos en data:
number
ID del paquete: el id en Listar paquetes.
string
La dirección que cubre el paquete.
string
Estado del paquete después del evento: active o completed en package.transfer, completed en package.completed, active en package.activated, expired en package.expired.
number
Transferencias con las que se compró el paquete.
number
Transferencias que le quedan a este paquete después del cambio que informa el evento: un número absoluto, no una diferencia.
Los eventos pueden llegar desordenados. Cada uno se entrega por separado, con reintentos durante unos 35 segundos, así que el package.completed causado por una transferencia puede llegar antes que el package.transfer de esa transferencia, y un package.transfer puede llegar antes que package.activated. Para cada package_id, conserve el remaining más bajo que haya visto: nunca aumenta. completed y expired son definitivos.
remaining también puede bajar sin evento. Un paquete de varias transferencias consume una transferencia al día como suscripción diaria —la primera al activarse, por eso un paquete de 500 transferencias empieza con remaining: 499— y ese cargo no se notifica. Dos transferencias desde la misma dirección en el mismo bloque pueden informar el mismo remaining. Tome remaining como una indicación y Listar paquetes como la fuente de verdad.
Para reconocer un reintento, use (package_id, tx_hash) en package.transfer y (event, package_id) en los demás: un reintento trae el mismo cuerpo, el mismo timestamp y la misma firma.

package.transfer

Una transferencia desde la dirección se pagó con un paquete. Se envía una vez por cada paquete al que se cargó la transferencia: casi siempre un evento por transferencia; una transferencia que tomó la última transferencia de un paquete y continuó en el siguiente genera un evento por cada uno.
string
La transacción de la transferencia en la cadena.
number
Transferencias tomadas de este paquete en esta transacción. Una transferencia a una dirección sin USDT necesita el doble de energía y cuenta como 2, que pueden repartirse entre dos paquetes.
Aquí status es completed cuando esta transferencia agotó el paquete.
Response

package.completed

El paquete dejó de cubrir transferencias.
string
transfer: una transferencia agotó el paquete; subscription: lo agotó el cargo diario; manual: soporte cerró el paquete. Tras manual, remaining puede ser mayor que cero, pero esas transferencias ya no se pueden usar.
string | null
Con reason: "transfer", la transferencia que cerró el paquete; null en los demás casos.
Un paquete sin saldo —por ejemplo, un paquete normal de una transferencia cuyo primer cargo diario tomó su única transferencia— se cierra con la siguiente transferencia sin un package.transfer propio: solo recibirá package.completed con el tx_hash de esa transferencia.
Response

package.activated

El paquete se activó y desde ahora sus transferencias reciben energía. Se envía en cada activación, incluida una compra con saldo, cuya respuesta ya dice active, y el pago de una factura, que activa el paquete poco después de que el pago se ve en la cadena.
string
Cómo se pagó el paquete: exactamente balance, TRX o USDT.
Response

package.expired

La factura de un paquete no se pagó dentro de su plazo de 30 minutos. No se cobró nada; compre de nuevo para obtener una factura nueva. Sin campos adicionales; remaining es igual a quota_total y no se puede usar.
Response

Entrega

  • Hasta 3 intentos, con pausas de 1 segundo y luego 4 segundos entre ellos. No hay pausa tras un éxito ni tras el último intento.
  • Tiempo de espera de 10 segundos por intento.
  • Esperamos cualquier 2xx. Cualquier otra cosa cuenta como intento fallido.
  • No se siguen las redirecciones. Un 301/302 es un intento fallido, no un salto.
  • Los eventos perdidos no se reenvían después. Si fallan los tres intentos, ese evento se pierde; concilie con Consultar pedido o Listar paquetes en lugar de esperar una repetición.
Su URL debe resolverse a una dirección pública. Se rechazan los rangos loopback, privados y link-local, y también un host que no resuelve: en ambos casos no se envía nada y no se escribe ninguna fila en el registro.

Verificación de la firma

Cada petición lleva X-Webhook-Signature: sha256=<hex>, un HMAC-SHA256 sobre el cuerpo bruto de la petición con su secreto de webhook como clave. Dos detalles deciden si su comprobación funciona:
  1. Quite el prefijo sha256= antes de comparar. La cabecera no es un digest hexadecimal a secas.
  2. Aplique el hash a los bytes recibidos, no a un objeto vuelto a serializar. Analizar el JSON y volcarlo de nuevo cambia el orden de las claves y los espacios, y la firma no coincidirá nunca.
Su secreto de firma aparece en Dashboard → API junto a la URL del webhook. No se devuelve a las peticiones con clave API: para leerlo hay que iniciar sesión en el sitio web.
Verifique la firma antes de actuar sobre el contenido y compare en tiempo constante (hmac.compare_digest / crypto.timingSafeEqual). Comparar con == filtra información por tiempo.