Webhooks
Firma HMAC, reintentos, y por qué el polling sigue siendo tu red.
Registrar el endpoint
Dinos a dónde avisarte y qué eventos te importan:
curl -X POST https://api.tikin.is/v1/webhook_endpoints \
-H "Authorization: Bearer tk_live_…" \
-H "Idempotency-Key: $(uuidgen | tr 'A-Z' 'a-z')" \
-H "Content-Type: application/json" \
-d '{
"url": "https://tuapp.com/hooks/tikin",
"events": ["payout.completed", "payout.failed", "payout.returned", "bonus_batch.ready"]
}'La respuesta trae el secret de firma, que se muestra UNA sola vez. Guárdalo
en tu almacén de secretos ya — no se puede volver a consultar.
Los eventos disponibles: transfer.completed · payout.completed ·
payout.failed · payout.returned · bonus_batch.ready ·
bonus_batch.completed · account.verified.
Verificar la firma
Cada entrega llega firmada con HMAC-SHA256 en el header Tikin-Signature.
Verifica ANTES de procesar — un webhook sin verificar es un endpoint público que
mueve tu lógica de dinero:
import hmac, hashlib
def verificar(secret: str, cuerpo_crudo: bytes, firma_recibida: str) -> bool:
esperada = hmac.new(secret.encode(), cuerpo_crudo, hashlib.sha256).hexdigest()
return hmac.compare_digest(esperada, firma_recibida)Usa el cuerpo crudo del request (bytes, tal como llegó), no el JSON re-serializado — cualquier reordenamiento de llaves rompe la firma.
Reintentos
Responde 2xx rápido (encola y procesa aparte). Si no respondes 2xx,
reintentamos con backoff — lo que implica que puedes recibir el mismo evento
dos veces: procesa por id de evento, idempotente, igual que nos exiges a
nosotros.
El polling es tu red de seguridad
Los webhooks avisan; no son la fuente de verdad. Tu endpoint puede estar caído
justo cuando el payout falló. La regla: todo estado que te importe debe poder
reconstruirse por polling (GET /v1/payouts/{id}, GET /v1/bonus_batches/{id}).
Un cron que revisa lo que lleva mucho tiempo "en vuelo" te salva el día que el
webhook no llegó — y ese día llega.
Si dudas, lee /v1/events — el log es la verdad
Todo evento que te enviamos (y los que tu endpoint se perdió) queda en el log de eventos: consultable, paginado por cursor, filtrable por tipo y fecha. Es la pieza de reconciliación definitiva:
curl "https://api.tikin.is/v1/events?type=payout.failed&created_at\[gte\]=2026-09-08T00:00:00Z" \
-H "Authorization: Bearer tk_live_…"El patrón: un cron recorre el log desde el último cursor procesado y compara
contra lo que tus webhooks ya aplicaron. Nada se pierde, aunque tu endpoint
haya estado caído un día entero. Y para volver a recibir UNA entrega puntual,
POST /v1/events/{event_id}/redeliver la
reenvía firmada, con el mismo id — tu procesamiento idempotente la absorbe
sin duplicar nada.