Sygnet

Sygnet — Webhooks

Recibe una llamada automática cada vez que emites un certificado Receive an automatic call every time you issue a certificate

¿Para quién es esto? Para quienes quieren que Sygnet avise a otro sistema cuando se certifica algo: un servidor propio, un script, un pipeline de CI, una hoja de cálculo o un canal de Slack. Si solo quieres gestionar tus certificados desde el navegador, no necesitas webhooks. Who is this for? For those who want Sygnet to notify another system when something is certified: a custom server, a script, a CI pipeline, a spreadsheet, or a Slack channel. If you only manage your certificates from the browser, you don't need webhooks.

1. ¿Qué es un webhook? 1. What is a webhook?

Un webhook es una URL de tu servidor a la que Sygnet llama automáticamente cuando ocurre algo. En este caso: cuando emites un certificado nuevo. A webhook is a URL on your server that Sygnet calls automatically when something happens. In this case: when you issue a new certificate.

La diferencia con una API normal es quién inicia la conversación: The difference from a regular API is who starts the conversation:

API clásica (tú preguntas)Classic API (you ask)

Tu script le pregunta a Sygnet: "¿Hay certificados nuevos?" — tienes que hacerlo tú, cuando quieras saberlo. Your script asks Sygnet: "Any new certificates?" — you have to do it yourself, whenever you want to know.

Webhook (Sygnet te avisa)Webhook (Sygnet notifies you)

Sygnet llama a tu URL en el momento en que se emite el certificado. Tu script no tiene que hacer nada hasta que llegue la llamada. Sygnet calls your URL the moment the certificate is issued. Your script doesn't have to do anything until the call arrives.

Es como la diferencia entre llamar al banco para ver si ha llegado una transferencia, o pedir que te manden un SMS cuando llegue. It's like the difference between calling your bank to check if a transfer arrived, or asking them to send you a text when it does.

2. Casos de uso2. Use cases

📧

Notificación por emailEmail notification

Envía un email automático a tu cliente con el enlace al certificado y el PDF adjunto en cuanto se emite. Automatically send your client an email with the certificate link and PDF attachment as soon as it's issued.

📊

Registro en Google Sheets o base de datosLog to Google Sheets or database

Apunta automáticamente cada certificación nueva (hash, nivel SIA, fecha, etiqueta) en tu hoja de seguimiento interna. Automatically record every new SIA registration (hash, SIA level, date, label) in your internal tracking sheet.

🔄

Disparar un pipeline de CI/CDTrigger a CI/CD pipeline

Tras certificar un entregable, lanza automáticamente el siguiente paso del proceso: empaquetar, publicar o archivar. After certifying a deliverable, automatically launch the next step: package, publish, or archive.

💬

Mensaje a Slack o TeamsMessage to Slack or Teams

Avisa a tu equipo en tiempo real: "✅ Certificado emitido: Informe Q2 2026 (SIA 1)". Notify your team in real time: "✅ Certificate issued: Q2 2026 Report (SIA 1)".

🎬

Actualizar tu web o canalUpdate your website or channel

Si eres creador, publica automáticamente el badge o el enlace de verificación en tu web cada vez que certifiques un vídeo o artículo. As a creator, automatically publish the badge or verification link on your site every time you certify a video or article.

3. Configurar tu primer webhook 3. Setting up your first webhook

  1. 1

    Abre el Dashboard y busca la sección Webhooks Open the Dashboard and find the Webhooks section

    Está justo debajo de las Claves API. It's right below the API Keys section.

  2. 2

    Haz clic en + Nuevo e introduce un nombre y tu URL Click + New and enter a name and your URL

    El nombre es solo para ti (ej: pipeline-informes). La URL debe empezar por https://. The name is just for you (e.g. report-pipeline). The URL must start with https://.

  3. 3

    Copia el secret que aparece una sola vez Copy the secret shown only once

    Este secret es la contraseña con la que Sygnet firma cada llamada. Si lo pierdes, tendrás que eliminar el webhook y crear uno nuevo. Guárdalo ahora. This secret is the password Sygnet uses to sign every call. If you lose it, you'll need to delete the webhook and create a new one. Save it now.

  4. 4

    Emite un certificado desde Sygnet Issue a certificate from Sygnet

    Tu servidor recibirá la llamada en pocos segundos. Comprueba los logs de tu aplicación para ver el payload. Your server will receive the call within seconds. Check your application logs to see the payload.

Límite: puedes crear hasta 5 webhooks por cuenta. Si necesitas notificar a varios sistemas, hazlo desde tu servidor receptor en lugar de crear un webhook por sistema. Limit: you can create up to 5 webhooks per account. If you need to notify multiple systems, do it from your receiving server rather than creating one webhook per system.

4. El evento que recibirás 4. The event you will receive

Cuando emites un certificado, Sygnet hace un POST a tu URL con este cuerpo JSON y estas cabeceras: When you issue a certificate, Sygnet sends a POST to your URL with this JSON body and these headers:

Cabeceras de la peticiónRequest headers

Content-Type: application/json
X-Sygnet-Signature: sha256=a3f9b2c1...

Cuerpo JSONJSON body

{
  "event":         "cert.created",
  "hash":          "a1b2c3d4...",          // SHA3-512, 128 chars
  "sia_level":     1,                      // 1=100% human … 5=100% AI
  "description":   "Q2 2026 Report",       // label you set when certifying, may be null
  "caduca_at":     "2027-06-06T10:00:00",  // expiry date (UTC), null if you didn't set one
  "issued_at":     "2026-06-06T10:00:00Z", // issue date (UTC)
  "published_url": "https://...",          // the URL you passed, or null
  "content_type":  "video"                 // manual or auto-detected from published_url, or null
}

Campos explicadosFields explained

event
Siempre cert.created. En el futuro puede haber otros eventos. Always cert.created. More event types may be added in the future.
hash
Identificador único del certificado. Úsalo para construir la URL de verificación o la URL del PDF. Unique certificate identifier. Use it to build the verification URL or the PDF URL.
sia_level
Nivel de atribución IA/humano — SIA (Synthetic Intelligence Attribution): 1 (100% humano) · 2 (humano con IA auxiliar) · 3 (coautoría humano-IA) · 4 (principalmente IA) · 5 (100% IA). AI/human attribution level — SIA (Synthetic Intelligence Attribution): 1 (100% human) · 2 (human with AI assist) · 3 (human-AI co-authorship) · 4 (mainly AI) · 5 (100% AI).
caduca_at
Fecha hasta la que el certificado es válido. Después de esta fecha el estado pasa a expired. Date until which the certificate is valid. After this date the status becomes expired.
URLs útiles a partir del hash:
API (JSON, para tu script): https://mysygnet.com/api/certificados/{hash}
PDF: https://mysygnet.com/api/certificados/{hash}/pdf
Badge: https://mysygnet.com/api/certificados/{hash}/badge
Para compartir con humanos (no el JSON de arriba), construye https://mysygnet.com/?cert=<base64({"h","s","e","l"})> — ver la Guía de automatización, sección 4 para el código exacto.
Useful URLs from the hash:
API (JSON, for your script): https://mysygnet.com/api/certificados/{hash}
PDF: https://mysygnet.com/api/certificados/{hash}/pdf
Badge: https://mysygnet.com/api/certificados/{hash}/badge
To share with humans (not the JSON above), build https://mysygnet.com/?cert=<base64({"h","s","e","l"})> — see the Automation Guide, section 4 for the exact code.

5. Verificar que viene de Sygnet 5. Verifying it comes from Sygnet

Cualquiera podría hacer un POST a tu URL. Para saber que la llamada realmente viene de Sygnet, verifica la firma. Anyone could POST to your URL. To confirm the call really comes from Sygnet, verify the signature.

Cada llamada incluye la cabecera X-Sygnet-Signature: sha256=<valor>. Ese valor es un HMAC-SHA256 calculado sobre el cuerpo completo de la petición usando el secret que guardaste al crear el webhook. Si no coincide, descarta la petición. Every call includes the X-Sygnet-Signature: sha256=<value> header. That value is an HMAC-SHA256 computed over the full request body using the secret you saved when creating the webhook. If it doesn't match, discard the request.

Python

import hmac, hashlib

def verify_signature(body: bytes, header_sig: str, secret: str) -> bool:
    """
    body       → raw request body (bytes, before parsing JSON)
    header_sig → value of X-Sygnet-Signature
    secret     → the secret you copied when creating the webhook
    """
    expected = "sha256=" + hmac.new(
        secret.encode(), body, hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(expected, header_sig)

Node.js

const crypto = require('crypto');

function verifySignature(body, headerSig, secret) {
  // body must be the raw Buffer or string, not the already-parsed JSON object
  const expected = 'sha256=' + crypto
    .createHmac('sha256', secret)
    .update(body)
    .digest('hex');
  return crypto.timingSafeEqual(
    Buffer.from(expected),
    Buffer.from(headerSig)
  );
}
Importante: usa siempre hmac.compare_digest (Python) o crypto.timingSafeEqual (Node.js) para comparar. Una comparación directa con == es vulnerable a ataques de temporización: un atacante podría deducir el secret probando variaciones. Important: always use hmac.compare_digest (Python) or crypto.timingSafeEqual (Node.js) to compare. A direct comparison with == is vulnerable to timing attacks: an attacker could deduce the secret by testing variations.
La firma se calcula sobre el cuerpo en crudo (los bytes exactos que Sygnet envía), no sobre el JSON ya parseado. Asegúrate de leer el body como bytes antes de pasarlo a la función de verificación. En Flask: request.get_data(). En Express: usa express.raw() o guarda el body original antes del JSON parser. The signature is computed over the raw body (the exact bytes Sygnet sends), not over the already-parsed JSON. Make sure to read the body as bytes before passing it to the verification function. In Flask: request.get_data(). In Express: use express.raw() or save the original body before the JSON parser.

6. Reintentos y desactivación automática 6. Retries and automatic deactivation

¿Qué pasa si tu servidor falla? What happens if your server fails?

1

Sygnet espera respuesta hasta 5 segundos. Si no responde o devuelve un error 5xx, cuenta como fallo. Sygnet waits for a response for up to 5 seconds. If there's no response or a 5xx error, it counts as a failure.

2

Reintenta 3 veces con espera entre intentos: 0 s → 1 s → 2 s. Retries 3 times with exponential backoff: 0 s → 1 s → 2 s.

!

Si acumula 5 fallos consecutivos (en distintas emisiones), el webhook se desactiva automáticamente para no desperdiciar recursos. If it accumulates 5 consecutive failures (across different issuances), the webhook is automatically deactivated to avoid wasting resources.

Respuestas que Sygnet considera exitosas Responses Sygnet considers successful

En la práctica, Sygnet cuenta como entrega correcta cualquier código por debajo de 500 (2xx, 3xx e incluso 4xx) — solo un 5xx o la ausencia de respuesta cuentan como fallo y disparan reintento. Aun así, te recomendamos responder siempre 2xx para que tu propio 4xx no quede enmascarado como "entregado". In practice, Sygnet counts any status code below 500 (2xx, 3xx, even 4xx) as a successful delivery — only a 5xx or no response counts as a failure and triggers a retry. Even so, we recommend always responding with 2xx so your own 4xx isn't masked as "delivered".

# Flask — respuesta mínima aceptableminimum acceptable response
return "", 200

# Express
res.sendStatus(200);

Reactivar un webhook desactivado Reactivating a deactivated webhook

En el Dashboard → Webhooks verás el estado Pausado y un botón Activar. Al reactivar, el contador de fallos se reinicia a cero. In Dashboard → Webhooks you'll see the Paused status and an Activate button. On reactivation, the failure counter resets to zero.

Antes de reactivar, asegúrate de que tu servidor está operativo y respondiendo correctamente. Before reactivating, make sure your server is running and responding correctly.

7. Servidor de ejemplo 7. Example server

Los ejemplos siguientes reciben el webhook, verifican la firma y registran el evento. Son el punto de partida mínimo — a partir de aquí añade tu lógica (enviar email, actualizar BD, etc.). The following examples receive the webhook, verify the signature, and log the event. This is the minimal starting point — from here, add your own logic (send email, update DB, etc.).

Python — Flask

import hmac, hashlib, json
from flask import Flask, request, abort

app    = Flask(__name__)
SECRET = "your_secret_here"   # the secret you copied from the Dashboard

@app.route("/webhook", methods=["POST"])
def receive():
    body      = request.get_data()          # raw bytes — BEFORE parsing
    signature = request.headers.get("X-Sygnet-Signature", "")
    expected  = "sha256=" + hmac.new(
        SECRET.encode(), body, hashlib.sha256
    ).hexdigest()

    if not hmac.compare_digest(expected, signature):
        abort(401)                          # invalid signature → reject

    event = json.loads(body)
    print(f"Certificate issued: {event['description']} "
          f"(SIA {event['sia_level']}) — hash: {event['hash'][:16]}…")

    # Add your logic here: send email, log to DB, notify Slack…

    return "", 200


if __name__ == "__main__":
    app.run(port=5000)

Instala con: pip install flask. Expón tu servidor con ngrok si estás en local durante pruebas. Install with: pip install flask. Expose your server with ngrok if you're testing locally.

Node.js — Express

const express = require('express');
const crypto  = require('crypto');

const app    = express();
const SECRET = 'your_secret_here';   // the secret you copied from the Dashboard

// Keep raw body BEFORE express parses it as JSON
app.use(express.raw({ type: 'application/json' }));

app.post('/webhook', (req, res) => {
  const signature = req.headers['x-sygnet-signature'] || '';
  const expected  = 'sha256=' + crypto
    .createHmac('sha256', SECRET)
    .update(req.body)
    .digest('hex');

  if (!crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signature))) {
    return res.sendStatus(401);    // invalid signature → reject
  }

  const event = JSON.parse(req.body);
  console.log(`Certificate: ${event.description} `+
              `(SIA ${event.sia_level}) — ${event.hash.slice(0,16)}…`);

  // Add your logic here: send email, log to DB, notify Slack…

  res.sendStatus(200);
});

app.listen(3000, () => console.log('Listening on :3000'));

Instala con: npm install express. Install with: npm install express.

Ejemplo completo — notificación por email (Python) Full example — email notification (Python)

Envía un email al cliente con el enlace al PDF del certificado. Sends the client an email with the link to the certificate PDF.

import hmac, hashlib, json, smtplib
from email.mime.text import MIMEText
from flask import Flask, request, abort

app         = Flask(__name__)
SECRET      = "your_secret_here"
SMTP_HOST   = "smtp.gmail.com"
SMTP_PORT   = 587
SMTP_USER   = "you@gmail.com"
SMTP_PASS   = "app_password"
DESTINATION = "client@company.com"
BASE_URL    = "https://mysygnet.com"

def verify(body, sig):
    esp = "sha256=" + hmac.new(SECRET.encode(), body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(esp, sig)

SIA_LABELS = {
    1: "100% human authorship",
    2: "Human with AI assist",
    3: "Human-AI co-authorship",
    4: "Mainly AI",
    5: "100% AI generated"
}

def send_email(event):
    hash_  = event["hash"]
    desc   = event.get("description", "—")
    level  = event["sia_level"]
    pdf    = f"{BASE_URL}/api/certificados/{hash_}/pdf"
    verify_url = f"{BASE_URL}/api/certificados/{hash_}"

    sia_label = SIA_LABELS.get(level, f"SIA Level {level}")

    body = f"""Dear client,

A SIA certificate has been issued for the document:

  Document:    {desc}
  Attribution: {sia_label}
  Expires:     {event.get('caduca_at','—')}

Verify its authenticity here:
{verify_url}

Download the official SIA certificate PDF:
{pdf}

Regards,
Sygnet Protocol
"""
    msg = MIMEText(body)
    msg["Subject"] = f"Certificate issued: {desc}"
    msg["From"]    = SMTP_USER
    msg["To"]      = DESTINATION

    with smtplib.SMTP(SMTP_HOST, SMTP_PORT) as s:
        s.starttls()
        s.login(SMTP_USER, SMTP_PASS)
        s.send_message(msg)

@app.route("/webhook", methods=["POST"])
def receive():
    body = request.get_data()
    if not verify(body, request.headers.get("X-Sygnet-Signature", "")):
        abort(401)
    send_email(json.loads(body))
    return "", 200

8. Preguntas frecuentes 8. FAQ

¿Puedo usar un webhook con Make, Zapier o n8n? Can I use a webhook with Make, Zapier, or n8n?

Sí. Estas plataformas generan una URL de webhook que puedes pegar directamente en Sygnet. Ojo: Make y Zapier no siempre permiten verificar la firma HMAC de forma nativa. Si la seguridad es importante, añade un nodo intermedio (una Cloud Function o un servidor propio) que verifique la firma antes de pasarlo a Make/Zapier. Para flujos internos de bajo riesgo, omitir la verificación es aceptable. Yes. These platforms generate a webhook URL you can paste directly into Sygnet. Note: Make and Zapier don't always support native HMAC signature verification. If security matters, add an intermediate node (a Cloud Function or your own server) that verifies the signature before passing it to Make/Zapier. For low-risk internal workflows, skipping verification is acceptable.

¿El webhook se activa también al renovar un certificado? Does the webhook fire when renewing a certificate?

No. El webhook solo se dispara con el evento cert.created, que corresponde a una emisión nueva (POST /api/certificados). Las renovaciones, revocaciones y borrados no generan evento de webhook por el momento. No. The webhook only fires for the cert.created event, which corresponds to a new issuance (POST /api/certificados). Renewals, revocations, and deletions don't generate webhook events at this time.

¿Qué pasa si mi servidor tarda en responder? What if my server takes too long to respond?

Sygnet espera 5 segundos. Si tu lógica (enviar email, consultar BD…) puede tardar más, responde 200 OK inmediatamente y procesa el evento en segundo plano (un hilo, una cola, un task worker). No hagas la lógica pesada antes de responder. Sygnet waits 5 seconds. If your logic (send email, query DB…) might take longer, respond 200 OK immediately and process the event in the background (a thread, queue, or task worker). Don't run heavy logic before responding.

¿Cómo pruebo el webhook en local sin tener un servidor público? How do I test the webhook locally without a public server?

Usa ngrok o webhook.site. Con ngrok: ejecuta ngrok http 5000 y pega la URL https://xxxxx.ngrok.io/webhook en Sygnet. Con webhook.site: te dan una URL temporal donde puedes ver las llamadas en tiempo real sin instalar nada. Use ngrok or webhook.site. With ngrok: run ngrok http 5000 and paste the URL https://xxxxx.ngrok.io/webhook into Sygnet. With webhook.site: get a temporary URL where you can see incoming calls in real time — no install needed.

Perdí el secret, ¿puedo recuperarlo? I lost the secret — can I recover it?

No. El secret se muestra una sola vez al crear el webhook y no se almacena de forma recuperable. Si lo perdiste: elimina el webhook desde el Dashboard y crea uno nuevo con la misma URL. El nuevo secret será diferente — actualízalo también en tu servidor. No. The secret is shown only once when creating the webhook and is not stored in a recoverable way. If you lost it: delete the webhook from the Dashboard and create a new one with the same URL. The new secret will be different — update it on your server too.

← Volver al Dashboard← Back to Dashboard  ·  Documentación APIAPI Docs  ·  Sygnet