API de telemetría de Pulse
POST /api/v1/pulse/telemetry es el endpoint que Clione Pulse — y cualquier scanner de terceros — usa para reportar un scan de página única de vuelta a Clione. El endpoint es solo-escritura: acepta un payload, lo guarda y devuelve 204 No Content. Para leer scans de vuelta, usa los endpoints del dashboard (scope auth) o GET /api/v1/public/pulse/badge para badges públicos.
Para el producto Pulse completo, mira Pulse dashboard y Extensión de Pulse.
Cuándo usarlo
Estás integrando un scanner que no es Chrome (paso de CI, script Node, crawler custom, herramienta de partner) y quieres que los resultados aparezcan en tu dashboard de Clione Pulse. La extensión de Chrome ya llama a este endpoint — no necesitas hacer nada para opt in ahí.
Si solo quieres leer scans para un dominio (badges, scoreboards públicos), mira los endpoints públicos de Pulse, no este.
Endpoint
POST /api/v1/pulse/telemetry
Content-Type: application/json
URL base de producción: https://api.clione.ai.
El endpoint responde con 204 No Content en éxito. Modos de fallo:
| Código | Significado |
|---|---|
400 | JSON malformado, faltan campos obligatorios. |
401 | Faltan headers de autenticación o son inválidos. |
401 | Mismatch de firma HMAC, timestamp obsoleto (> 5 min de skew) o token revocado. |
500 | La persistencia falló. El body contiene { error, detail }. |
Autenticación — dos modos
El endpoint acepta cualquiera de dos modos de auth mutuamente exclusivos. Elige el que coincida con tu contexto.
Modo 1 — Token público (X-Pulse-Token)
Usado por la build pública de la Chrome Web Store de la extensión de Pulse y por cualquiera que se diera de alta vía la landing page pública de Pulse. El modo más simple.
Headers:
X-Pulse-Token: <PulseLead.apiToken>
Cómo conseguir un token:
- Haz que tu usuario se dé de alta en la landing page pública de Pulse (o vía el popup de la extensión de Chrome).
- El sistema emite un
apiTokenopaco ligado a un registroPulseLead. - El token está verificado, el apiToken es lo que pones en el header.
Formato del token: string aleatorio opaco de 32+ bytes. No hay HMAC — el token en sí es el secret. Trátalo como una contraseña, no lo commitees al código fuente.
Rotación: contacta a tu superadmin de Clione. Un token rotado invalida el viejo de inmediato.
Modo 2 — API key firmada con HMAC (X-Pulse-Key + X-Pulse-Timestamp + X-Pulse-Signature)
Usado por las tools internas de Clione y por partners con una API key sk_live_* cuyos scopes incluyan pulse:scan o admin. Obligatorio cuando quieres el tier de rate-limit más alto.
Headers:
X-Pulse-Key: sk_live_<...>
X-Pulse-Timestamp: <milisegundos epoch, tiempo actual>
X-Pulse-Signature: <HMAC-SHA256 en hex>
Donde:
X-Pulse-Timestampes el tiempo actual en milisegundos epoch. El servidor rechaza peticiones donde|now - timestamp| > 5 minutos(ventana de replay).X-Pulse-SignatureesHMAC-SHA256(apiKey, "${timestamp}.${rawJsonBody}"), codificado en hex. El cuerpo JSON crudo — bytes tal cual se envían — es lo que se firma; no re-stringifiques.
El servidor busca la API key por su hash SHA-256, luego recalcula el HMAC usando la key cruda del header. Usa la semántica de crypto.timingSafeEqual también en tu lado si construyes un verificador.
Payload
{
"url": "https://shop.example.com/products/widget-001",
"hostname": "shop.example.com",
"scannedAt": "2026-06-05T14:23:45.000Z",
"platform": { "platform": "shopify", "confidence": 0.95 },
"pageType": "product",
"clione": { "detected": true, "version": "0.4.1" },
"signals": {
"title": { "value": "Widget 001 — Acme", "length": 21 },
"description": { "value": "...", "length": 142 },
"canonical": { "value": "https://shop.example.com/products/widget-001" },
"robots": "index,follow",
"lang": "en",
"headings": { "h1": ["Widget 001"], "h2": ["Specifications"] },
"openGraph": {
"og:title": "Widget 001 — Acme",
"og:type": "product",
"og:image": "..."
},
"jsonLd": [
{ "@context": "https://schema.org", "@type": "Product", "name": "Widget 001", "sku": "W-001", "brand": { "name": "Acme" } }
]
},
"signalPresence": { "title": true, "description": true, "jsonLd": true, "openGraph": true, "h1": true, "canonical": true, "robots": true },
"quality": {
"grade": "B",
"percentage": 82,
"title": 2, "description": 2, "keywords": 1, "jsonLd": 2, "openGraph": 1
},
"country": "ES",
"vertical": "ecommerce"
}
Referencia de campos (solo los obligatorios-para-persistir se listan como obligatorios; el resto son best-effort y se degradan con elegancia):
| Campo | Obligatorio | Tipo | Notas |
|---|---|---|---|
url | recomendado | string | La URL completa con path. Quita query string y hash antes de enviar. Si está ausente, el servidor cae a https://${hostname}/. |
hostname | sí | string | Host pelado, sin esquema, sin path. |
scannedAt | recomendado | ISO-8601 | Por defecto el now del servidor si se omite. |
platform.platform | recomendado | string | shopify, bigcommerce, woocommerce, magento, custom, unknown. |
pageType | recomendado | string | product, collection, category, page, home, cart, checkout, unknown. |
clione.detected | recomendado | boolean | True si el scanner encontró el huella del embed o schema de Clione. |
signals | sí | object | Señales crudas extraídas. La forma de arriba refleja lo que la extensión de Chrome envía. |
signalPresence | recomendado | object | Mapa booleano. Usado por el dashboard para agregaciones rápidas de presencia. |
quality.grade | recomendado | A–F | La calificación propia del scanner. Si está ausente, por defecto F. |
quality.percentage | recomendado | 0–100 | El score propio del scanner. Si está ausente, por defecto 0. |
country | opcional | ISO 3166-1 alpha-2 | Usado por filtros del dashboard. Si está ausente, el servidor usa GeoIP desde la IP de la petición. |
vertical | opcional | string | Free-form, usado para segmentación de analytics de tenant. |
Cualquier cosa no listada se preserva en el blob JSON crudo data — puedes enviar campos diagnósticos arbitrarios y persistirán para inspección, simplemente no estarán indexados.
Ejemplos curl
Modo 1 — Token público
curl -X POST https://api.clione.ai/api/v1/pulse/telemetry \
-H "Content-Type: application/json" \
-H "X-Pulse-Token: $PULSE_TOKEN" \
--data @scan-payload.json
Modo 2 — API key firmada con HMAC
TS=$(node -e 'process.stdout.write(Date.now().toString())')
BODY=$(cat scan-payload.json)
SIG=$(node -e "
const c = require('crypto');
const ts = process.argv[1];
const body = process.argv[2];
const key = process.env.PULSE_API_KEY;
process.stdout.write(c.createHmac('sha256', key).update(\`\${ts}.\${body}\`).digest('hex'));
" "$TS" "$BODY")
curl -X POST https://api.clione.ai/api/v1/pulse/telemetry \
-H "Content-Type: application/json" \
-H "X-Pulse-Key: $PULSE_API_KEY" \
-H "X-Pulse-Timestamp: $TS" \
-H "X-Pulse-Signature: $SIG" \
--data "$BODY"
Una llamada exitosa devuelve 204 No Content con body vacío.
Rate limits
Este endpoint hereda los mismos rate limits basados en tier que el resto de /api/v1/pulse/*:
| Tier | Cómo se reconoce | Scans por minuto |
|---|---|---|
| anonymous | sin headers de auth, o token no reconocido | 10 |
| authed | X-Pulse-Token válido para un lead verificado | 50 |
| agent | X-Pulse-Key con scope pulse:scan o admin | 1000 |
429 Too Many Requests incluye un body JSON indicando el tier activo y el límite para que puedas hacer backoff apropiadamente.
Idempotencia y dedup
El endpoint no es idempotente — enviar el mismo payload dos veces crea dos filas PulseScan. Es intencional: un re-scan tras un deploy es un evento real.
Si necesitas dedup, dedupea en el lado cliente, o usa el endpoint bulk (POST /pulse/scan-bulk) que hace skipDuplicates en el insert por (url, scannedAt).
Relacionados
- Pulse dashboard — el agregador de cara al comerciante.
- Extensión de Pulse — la extensión de Chrome que llama a este endpoint por defecto.
- Autenticación API — para emitir keys
sk_live_*con scopepulse:scan.