Saltar al contenido

Actualizado el 12 oct 2026

Webhooks firmados

Envía eventos de tus solicitudes a tus sistemas y verifica la firma HMAC-SHA256 de cada envío.

Un webhook le avisa a otro sistema, como Zapier o tu hoja de producción, cuando algo pasa en una solicitud. Cada envío va firmado con un secreto para que compruebes que viene de Promptusphere. Solo Dueña o dueño y Administración pueden configurarlos.

Agrega un endpoint

  1. 1. Ve a Ajustes, Webhooks y elige Agregar endpoint.
  2. 2. Escribe la URL del endpoint. Debe empezar con https:// y ser pública.
  3. 3. Elige los Eventos: request.created, request.status_changed, request.ready, request.completed, approval.recorded o quote.decided.
  4. 4. Guarda. Te mostramos el secreto de firma una sola vez: elige Copiar secreto, guárdalo en tu sistema y elige Ya lo guardé.
  5. 5. Elige Enviar evento de prueba para confirmar que llega.

Verifica la firma

Cada envío incluye este encabezado:

Promptusphere-Signature: t=<unix seconds>,v1=<hex>
  1. 1. Separa t (segundos desde 1970) y v1 (la firma en hexadecimal).
  2. 2. Rechaza el evento si la diferencia entre la hora actual y t es mayor a 300 segundos (5 minutos). Así evitas repeticiones.
  3. 3. Calcula HMAC-SHA256 con tu secreto sobre el texto t + . + el cuerpo crudo de la petición, antes de convertirlo de JSON. Expresa el resultado en hexadecimal.
  4. 4. Compara tu resultado con v1 en tiempo constante. Si no coinciden, responde con un error y no proceses el evento.

Este ejemplo en JavaScript usa Web Crypto (crypto.subtle):

// Verifies the Promptusphere-Signature header with Web Crypto.
// Works in Cloudflare Workers, Deno and Node 20+ (in Node 18, use webcrypto from node:crypto).
const TOLERANCE_SECONDS = 300;
const encoder = new TextEncoder();

function toHex(buffer) {
  return [...new Uint8Array(buffer)].map((b) => b.toString(16).padStart(2, "0")).join("");
}

function timingSafeEqual(a, b) {
  if (a.length !== b.length) return false;
  let diff = 0;
  for (let i = 0; i < a.length; i++) diff |= a.charCodeAt(i) ^ b.charCodeAt(i);
  return diff === 0;
}

export async function verifyPromptusphere(rawBody, header, secret) {
  if (!header) return false;
  const parts = {};
  for (const piece of header.split(",")) {
    const [key, value] = piece.trim().split("=");
    if (key && value) parts[key] = value;
  }
  const t = Number(parts.t);
  if (!Number.isInteger(t) || !parts.v1) return false;

  const now = Math.floor(Date.now() / 1000);
  if (Math.abs(now - t) > TOLERANCE_SECONDS) return false;

  const key = await crypto.subtle.importKey(
    "raw",
    encoder.encode(secret),
    { name: "HMAC", hash: "SHA-256" },
    false,
    ["sign"],
  );
  const mac = await crypto.subtle.sign("HMAC", key, encoder.encode(t + "." + rawBody));
  return timingSafeEqual(toHex(mac), parts.v1);
}

// Example: a Cloudflare Worker that receives the webhook.
export default {
  async fetch(request, env) {
    const rawBody = await request.text(); // raw body, before JSON.parse
    const header = request.headers.get("Promptusphere-Signature");
    const ok = await verifyPromptusphere(rawBody, header, env.PROMPTUSPHERE_WEBHOOK_SECRET);
    if (!ok) return new Response("invalid signature", { status: 400 });

    const event = JSON.parse(rawBody);
    // event.id, event.type (for example request.ready), event.createdAt, event.data
    return new Response("ok");
  },
};

Entregas y reintentos

  • Responde con un código 2xx en menos de 10 segundos. Cualquier otro resultado cuenta como fallo.
  • Reintentamos los envíos fallidos varias veces, con esperas cada vez más largas.
  • En Ver entregas está el Registro de entregas con las últimas 50, su resultado y sus intentos. Usa Reenviar para mandar una de nuevo.
  • Cada envío trae también los encabezados Promptusphere-Event y Promptusphere-Delivery. Usa el id del evento para ignorar duplicados.

soporte@promptusphere.com

¿Sigues con dudas?

Escríbenos con el nombre de la solicitud o la pantalla donde estabas y te ayudamos a revisarlo.

Incluye el nombre de la solicitud o la página donde pasó. Entre 10 y 4,000 caracteres.

Verificación contra abuso

Cargando la verificación…