Ir al contenido

API REST (Tool API y Server-to-Server)

El SDK de navegador se comunica con la Tool API. Puede llamar a la misma API desde su propio backend: para registrar consentimientos recogidos en su propia interfaz, para emitir tokens de aprobación de un solo uso o para consultar y verificar registros.

URL base: https://system.hashentry.com

MétodoRutaFinalidadAutenticación
GET/tool/v1/configConfiguración del banner, documentos asociados, región detectadaClave API (lectura)
GET/tool/v1/documents/{slug}/activeVersión activa de un documento, incluido el contenidoClave API (lectura)
GET/tool/v1/documents/activeDocumentos activos asociados al sitioClave API (lectura)
POST/tool/v1/consentsRegistrar un consentimiento (ruta del SDK de navegador / móvil)Clave API (escritura) + firma
POST/tool/v1/consents/batchRegistrar hasta 100 consentimientosClave API (escritura) + firma
POST/tool/v1/s2s/consentsRegistrar un consentimiento desde su backendClave API (escritura) + JWT + firma
POST/tool/v1/s2s/consents/batchRegistrar hasta 100 consentimientos desde su backendClave API (escritura) + JWT + firma
POST/tool/v1/s2s/approval-tokensEmitir un token de aprobación de documento de un solo usoClave API (escritura) + JWT + firma
GET/tool/v1/consents/statusConsentimientos actuales de un visitanteClave API (lectura)
GET/tool/v1/consents/verify/{id}Comprobación de integridad de un registroClave API (lectura)
GET POST/api/v1/public/consents/verifyComprobación pública de integridad (sin clave)ninguna

Toda solicitud a la Tool API incluye la clave del sitio:

X-API-Key: he_live_…

Una clave tiene un ámbito: read permite los endpoints GET; write permite además POST. El sitio debe estar verificado en el panel. Si el sitio tiene una lista de orígenes permitidos, las solicitudes de navegador procedentes de otros orígenes se rechazan; si tiene una lista de direcciones IP permitidas, las solicitudes procedentes de otras direcciones se rechazan.

Firma de la solicitud (todas las solicitudes POST)

Sección titulada «Firma de la solicitud (todas las solicitudes POST)»
X-Signature: <HMAC-SHA256 en hexadecimal>
X-Timestamp: <hora Unix en segundos>
X-Nonce: <valor único por solicitud>
body_hash = SHA-256(cuerpo de la solicitud sin procesar) (hexadecimal en minúsculas)
canonical = METHOD + "\n" + PATH + "\n" + TIMESTAMP + "\n" + NONCE + "\n" + body_hash
signature = HMAC-SHA256(clave = clave API, mensaje = canonical) (hexadecimal en minúsculas)
  • METHOD va en mayúsculas. PATH es la ruta de la solicitud, incluido /tool/v1, sin la cadena de consulta; p. ej. /tool/v1/s2s/consents.
  • El hash del cuerpo se calcula sobre los bytes exactos que usted envía. Serialice el JSON una sola vez y envíe esa misma cadena.
  • X-Timestamp debe estar dentro de ±300 segundos respecto a la hora del servidor.
  • X-Nonce tiene entre 8 y 128 caracteres de A–Z a–z 0–9 . _ : - (un UUID sirve) y solo puede utilizarse una vez por sitio; una repetición se rechaza con NONCE_REPLAYED.

Ejemplo desarrollado (el vector de prueba compartido por el SDK y la API):

API key : he_live_0123456789abcdef0123456789abcdef
Method : POST
Path : /tool/v1/consents
Timestamp : 1760000000
Nonce : 3f2b8c1e-5a47-4d0e-9b6a-7c1d2e3f4a5b
Body : {"consent_type":"cookie","decision":"accepted","language":"tr","idempotency_key":"9a1c2b3d-4e5f-4a6b-8c7d-0e1f2a3b4c5d","metadata":{"note":"çerez ✓"}}
body_hash : 93316d8d6adb505a19668f4022e4e33b12aed9c0c4bb97d205b5064257090d34
canonical : POST\n/tool/v1/consents\n1760000000\n3f2b8c1e-5a47-4d0e-9b6a-7c1d2e3f4a5b\n93316d8d6adb505a19668f4022e4e33b12aed9c0c4bb97d205b5064257090d34
signature : e108683927badf35c36d5f467f63a9704c131d0102f14b06af7ed3046d44a8be

(\n representa un único carácter de salto de línea; el cuerpo está en UTF-8.) Si su implementación produce esta firma para estas entradas, es correcta.

Node.js
const crypto = require('crypto');
function sign(apiKey, method, path, body) {
const timestamp = Math.floor(Date.now() / 1000).toString();
const nonce = crypto.randomUUID();
const bodyHash = crypto.createHash('sha256').update(body, 'utf8').digest('hex');
const canonical = [method.toUpperCase(), path, timestamp, nonce, bodyHash].join('\n');
const signature = crypto.createHmac('sha256', apiKey).update(canonical, 'utf8').digest('hex');
return { 'X-Signature': signature, 'X-Timestamp': timestamp, 'X-Nonce': nonce };
}
const body = JSON.stringify({ consent_type: 'document_approval', decision: 'accepted', /* … */ });
const headers = {
'Content-Type': 'application/json',
'X-API-Key': apiKey,
Authorization: `Bearer ${jwt}`, // solo en las rutas S2S
...sign(apiKey, 'POST', '/tool/v1/s2s/consents', body),
};

La firma prueba que la solicitud se realizó con la clave API y que no se ha repetido. Dado que el SDK de navegador utiliza la misma clave, que es visible en el código fuente de la página, la firma por sí sola no prueba que una solicitud procediera de su backend: para eso están las rutas S2S.

Rutas de servidor a servidor (/tool/v1/s2s/…)

Sección titulada «Rutas de servidor a servidor (/tool/v1/s2s/…)»

Las solicitudes S2S necesitan tres cosas: una clave API de escritura, la firma de la solicitud y un JWT de un usuario con el rol api_user:

  1. Invite en el panel a un miembro del equipo con el rol api_user. Este rol solo puede utilizar la API S2S; no puede iniciar sesión en el panel.
  2. Inicie sesión con POST /api/v1/auth/login para obtener un JWT; renuévelo con POST /api/v1/auth/refresh.
  3. Envíe Authorization: Bearer <jwt> junto con X-API-Key y los encabezados de firma. El usuario debe pertenecer al tenant propietario del sitio.

Solo las solicitudes S2S pueden establecer ip_address, user_agent, otp_verified y otp_reference. En la ruta de navegador, la dirección IP y el agente de usuario se toman siempre de la conexión, y los campos OTP se ignoran.


POST /tool/v1/consents (o /tool/v1/s2s/consents)

{
"consent_type": "cookie",
"decision": "partial",
"document_version_id": "0190a1b2-…",
"language": "en",
"categories": { "necessary": true, "analytics": true, "marketing": false },
"idempotency_key": "9a1c2b3d-4e5f-4a6b-8c7d-0e1f2a3b4c5d",
"visitor_id": "user-4711",
"visitor_region": "DE",
"compliance_framework": "gdpr",
"metadata": { "order_id": "ORD-54321" }
}
CampoReglas
consent_typeObligatorio. Hasta 50 caracteres (A–Z a–z 0–9 _ - .). En uso: cookie, document_approval, do_not_sell_opt_out.
decisionObligatorio. accepted, rejected, partial o withdrawn.
document_version_idUUID de una versión activa de un documento asociado al sitio. Sin él, o con una versión que no está activa, la solicitud se rechaza con 409 CONFIG_OUTDATED. Un consentimiento de cookies hace referencia al documento cookie_policy del sitio.
languageSi se indica, debe ser el idioma de esa versión.
categoriesObjeto de clave de categoría → booleano. Solo se almacenan las categorías definidas en la configuración del banner.
idempotency_keyEntre 8 y 64 caracteres (A–Z a–z 0–9 . _ : -). Véase más abajo.
visitor_idSu referencia de la persona, hasta 100 caracteres.
visitor_region, visitor_countryHasta 10 / 2 caracteres.
compliance_frameworkkvkk, gdpr, uk_gdpr, ccpa, lgpd, popia, pipeda, pipl, pipa o none.
sourceHasta 20 caracteres, p. ej. client, server.
metadata, extra_fieldsObjetos con un máximo de 30 claves y unos 16 KB cada uno.
approval_tokenToken de un solo uso para un document_approval (véase más abajo).
Campos de firma / de documento personalizadosignature_image, signature_mime_type, signer_name, signer_email, signer_phone, signer_id_number, declaration_text, drawing_duration_ms, stroke_count, strokes_data, input_type, screen_resolution, custom_document_content, custom_document_mime_type; todos de nivel superior. Consulte Aprobación y firma de documentos.
ip_address, user_agent, otp_verified, otp_referenceSolo S2S.

Obtenga el document_version_id de GET /tool/v1/config?language=en (data.documents[].version.id) o de GET /tool/v1/documents/{slug}/active?language=en.

{
"message": "…",
"data": {
"consent_token": "ct_…",
"proof_hash": "…64 hex characters…",
"created_at": "2026-10-10T10:00:00+00:00",
"duplicate": false
}
}

202 significa que la solicitud ha superado todas las comprobaciones y que el registro se ha aceptado para su escritura. El token, el hash de prueba y la marca de tiempo son definitivos en ese momento, pero la fila la escribe un proceso en segundo plano, por lo que status y verify pueden no encontrarla durante un breve instante.

Envíe una clave por acción del usuario y reutilícela en cada reintento de esa acción. Una solicitud repetida con la misma clave no crea un segundo registro ni consume cuota; devuelve la respuesta original con "duplicate": true. Las claves se recuerdan durante 24 horas. El SDK de navegador genera un UUID por decisión y lo utiliza para el primer intento, los reintentos del cliente y los reenvíos desde su cola sin conexión.

POST /tool/v1/consents/batch con { "consents": [ { … }, … ] } (de 1 a 100 elementos, con los mismos campos que arriba).

{
"data": {
"accepted": 2,
"rejected": 1,
"results": [
{ "index": 0, "status": "accepted", "consent_token": "ct_…", "proof_hash": "…", "created_at": "…", "duplicate": false },
{ "index": 1, "status": "accepted", "consent_token": "ct_…", "proof_hash": "…", "created_at": "…", "duplicate": true },
{ "index": 2, "status": "rejected", "error": "…", "error_code": "CONSENT_LIMIT_EXCEEDED", "code": 429 }
]
}
}

El estado es 202 cuando el lote se ha procesado; compruebe cada entrada de results. Los errores de validación y un elemento con una versión de documento desconocida o inactiva provocan el rechazo de todo el lote (422 / 409).


POST /tool/v1/s2s/approval-tokens — solo S2S.

{
"document_slug": "service-contract",
"subject": "user-4711",
"signer_name": "Jane Doe",
"signer_email": "[email protected]",
"expires_in": 600
}
CampoReglas
document_slug / document_version_idAl menos uno. Con un identificador de versión, el token es válido únicamente para esa versión.
subjectObligatorio. Su referencia de la persona (máx. 100 caracteres); se almacena como el visitor_id del registro.
signer_name, signer_email, signer_phone, signer_id_numberOpcionales. Datos de identidad que usted avala.
otp_verified, otp_referenceOpcionales. Declaran una comprobación OTP que usted ha realizado.
custom_document_hashOpcional. SHA-256 (hexadecimal) de un documento generado por su sistema; la aprobación debe incluir entonces exactamente ese contenido.
expires_inSegundos, de 30 a 3600. Valor predeterminado: 600.

Respuesta 201:

{
"data": {
"approval_token": "hat_…",
"approval_token_id": "atk_…",
"expires_in": 600,
"expires_at": "2026-10-10T10:10:00+00:00",
"document_id": "…",
"document_slug": "service-contract",
"document_version_id": null
}
}

Entregue approval_token a la página y páselo al SDK (approvalToken) o envíelo como approval_token de nivel superior de un consentimiento document_approval. El servidor lo consume una sola vez, toma la identidad del token y registra identity_assurance: tenant_attested junto con el approval_token_id, que no es secreto. Una solicitud rechazada por otro motivo (límite de frecuencia, cuota) no consume el token. HashEntry almacena únicamente un hash del token.


Devuelve hasta 20 consentimientos no caducados de un visitor_id, del más reciente al más antiguo. Filtros opcionales: consent_type, document_slug, document_version_id.

{id} es el UUID de un consentimiento, un consent_token (ct_…) o un proof_hash.

{
"valid": true,
"data": {
"consent_type": "cookie",
"decision": "accepted",
"proof_hash": "…",
"hash_version": 2,
"hash_valid": true,
"merkle_valid": null,
"is_anonymized": false,
"is_tsa_stamped": false,
"created_at": "2026-10-10T10:00:00+00:00"
}
}

Qué significa esta respuesta y qué no:

  • valid: true significa que el registro pertenece a su tenant, que HashEntry ha recalculado el hash de prueba a partir del registro almacenado con la clave secreta de su tenant y coincide, y que, si el registro forma parte de un lote Merkle, su prueba de inclusión es correcta. La existencia de una fila no basta.
  • merkle_valid es null cuando el registro no tiene prueba Merkle.
  • is_tsa_stamped indica que existe un token de sello de tiempo para el registro (directamente o a través de su lote Merkle). No indica que la firma del token se haya verificado en esta llamada.
  • Se trata de una comprobación realizada por HashEntry sobre los datos de HashEntry. Para verificar sin confiar en la respuesta del servidor, exporte un paquete de evidencias y verifíquelo usted mismo: Verificación de evidencias sin conexión.
  • Un identificador desconocido devuelve 404 con "valid": false y code: "CONSENT_NOT_FOUND".

Sin clave API; con límite de frecuencia. Parámetros: token (un token ct_… o el valor de la cookie _he_consent del SDK), proof_hash o id. Devuelve la decisión, las categorías, el dominio del sitio, las marcas de tiempo, el estado del hash y la información del sello de tiempo, y nunca la dirección IP, el agente de usuario ni el identificador del visitante.

  • Un registro que existe: 200 con valid tal como se describe más arriba.
  • Un valor de cookie de navegador que no puede asociarse a un registro del servidor: 200 con "valid": false, "source": "client_token", "server_record_found": false y los campos decodificados de la cookie. La cookie no está firmada, por lo que nunca se notifica como válida por sí sola.
  • No se encuentra nada: 404 con "valid": false. Sin identificador: 422.

Las respuestas de error son JSON e incluyen un código de máquina estable en code (también en error_code y error.code; algunos errores de autenticación solo utilizan error.code).

HTTPCódigoSignificado
401MISSING_API_KEY, INVALID_API_KEYClave API ausente o desconocida.
401MISSING_SIGNATURE_HEADERS, TIMESTAMP_EXPIRED, INVALID_SIGNATURE, INVALID_NONCE, NONCE_REPLAYEDProblemas con la firma de la solicitud.
401MISSING_BEARER_TOKEN, INVALID_BEARER_TOKENS2S: JWT ausente o no válido.
403SITE_NOT_VERIFIEDVerifique el sitio en el panel.
403ORIGIN_NOT_ALLOWED, IP_NOT_ALLOWEDNo figura en la lista de permitidos del sitio.
403INSUFFICIENT_ROLE, TENANT_MISMATCHS2S: el usuario no es un api_user de este tenant.
403—El ámbito de la clave API no permite el método.
404DOCUMENT_NOT_FOUND, VERSION_NOT_FOUND, CONSENT_NOT_FOUND
409CONFIG_OUTDATEDdocument_version_id ausente, no activo, con un idioma incorrecto, o la caché de configuración del servidor está fría. Vuelva a leer /tool/v1/config; reenvíe solo si la versión sigue siendo la activa.
422VALIDATION_ERRORLos errores de campo están en errors.
422APPROVAL_TOKEN_REQUIRED, APPROVAL_TOKEN_INVALID, APPROVAL_TOKEN_CONFLICTConsulte tokens de aprobación de un solo uso.
422INVALID_CATEGORIES, METADATA_TOO_MANY_KEYS, METADATA_TOO_LARGE, EXTRA_FIELDS_TOO_MANY_KEYS, EXTRA_FIELD_NAME_TOO_LONG, EXTRA_FIELDS_TOO_LARGE, STROKES_DATA_TOO_LARGE, INVALID_SIGNATURE_IMAGE, SIGNATURE_IMAGE_TOO_LARGE, INVALID_CUSTOM_DOCUMENT, UNSUPPORTED_CUSTOM_DOCUMENT_TYPE, CUSTOM_DOCUMENT_TOO_LARGELímites de la carga útil.
429RATE_LIMIT_EXCEEDEDLímite de frecuencia según el plan. Véanse Retry-After, X-RateLimit-Limit, X-RateLimit-Remaining.
429CONSENT_LIMIT_EXCEEDED, NO_CONSENT_SUBSCRIPTIONLa cuota de consentimientos del plan se ha agotado / no hay ningún plan activo.
429TOO_MANY_INVALID_API_KEYSDemasiadas solicitudes con claves no válidas desde una misma dirección IP.
503QUEUE_OVERLOADED, ADMISSION_UNAVAILABLE, API_KEY_LOOKUP_UNAVAILABLETemporal. Reintente después de Retry-After con la misma idempotency_key.

Reintente las respuestas 408, 425, 429 y 5xx con el mismo cuerpo y la misma idempotency_key (y con una marca de tiempo, un nonce y una firma nuevos). No reintente sin cambios otras respuestas 4xx.