Recibe una llamada automática cada vez que emites un certificado Receive an automatic call every time you issue a certificate
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.
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.
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.
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://.
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.
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.
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
cert.created. En el futuro puede haber otros eventos.
Always cert.created. More event types may be added in the future.
expired.
Date until which the certificate is valid. After this date the status becomes expired.
https://mysygnet.com/api/certificados/{hash}https://mysygnet.com/api/certificados/{hash}/pdfhttps://mysygnet.com/api/certificados/{hash}/badgehttps://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:https://mysygnet.com/api/certificados/{hash}https://mysygnet.com/api/certificados/{hash}/pdfhttps://mysygnet.com/api/certificados/{hash}/badgehttps://mysygnet.com/?cert=<base64({"h","s","e","l"})> —
see the Automation Guide, section 4 for the exact code.
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)
);
}
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.
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.
¿Qué pasa si tu servidor falla? What happens if your server fails?
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.
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.
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
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.
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.
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.
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.
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.