Saltar a contenido

Webhooks

FarmAPI puede notificar un evento compacto por territorio cuando un sync termina.

Callback origin (solo dominio)

Configuras un callback como origin, por ejemplo:

  • https://tu-dominio.com

FarmAPI entregara siempre en:

  • https://tu-dominio.com/.well-known/farmaapi/webhook

Evento

Evento principal:

  • territory.sync.completed

Ejemplo de payload:

{
  "event": "territory.sync.completed",
  "event_id": "run-...:ES-MD:territory.sync.completed",
  "occurred_at": "2026-02-15T03:15:00Z",
  "run_id": "run-...",
  "territory_code": "ES-MD",
  "status": "success",
  "stats": { "fetched": 0, "upserts": 0, "deletes": 0, "published": 0, "rejected": 0 },
  "changed": false,
  "from_lkg": false
}

Firma (HMAC-SHA256)

Headers:

  • X-Event-ID
  • X-Timestamp (unix seconds)
  • X-Signature (hex sha256)

Firma:

  • signature = HMAC_SHA256(secret, "{timestamp}.{canonical_json_body}")

Recomendacion: verifica usando el raw body exacto recibido (bytes), sin re-serializar JSON.

Pseudocodigo:

  • message = "{timestamp}." + raw_body_as_utf8
  • expected = HMAC_SHA256_HEX(secret, message)
import crypto from "node:crypto";

export function verifyFarmaApiWebhook(req, secret) {
  const ts = req.header("X-Timestamp") || "";
  const sig = req.header("X-Signature") || "";
  const rawBody = req.rawBody.toString("utf8"); // necesitas body raw
  const msg = `${ts}.${rawBody}`;
  const expected = crypto.createHmac("sha256", secret).update(msg).digest("hex");
  return crypto.timingSafeEqual(Buffer.from(expected, "hex"), Buffer.from(sig, "hex"));
}
import hmac, hashlib

def verify(secret: str, ts: str, raw_body: bytes, signature_hex: str) -> bool:
  msg = ts.encode("utf-8") + b"." + raw_body
  expected = hmac.new(secret.encode("utf-8"), msg, hashlib.sha256).hexdigest()
  return hmac.compare_digest(expected, signature_hex)
<?php
function verify($secret, $ts, $rawBody, $sigHex) {
  $msg = $ts . "." . $rawBody;
  $expected = hash_hmac("sha256", $msg, $secret);
  return hash_equals($expected, $sigHex);
}