Referencia de la API

Endpoints, campos de respuesta, códigos de estado, límites y nuestros compromisos de estabilidad.

Referencia de la API (V2)

URL base: https://api.captchacore.eu: todos los endpoints responden con JSON. Si usa el widget suministrado, nunca llamará usted a /challenge; de eso se encarga el widget. Para un complemento propio solo /verify es obligatorio.

Autenticación

Endpoint Clave Dónde se usa
GET /api/v2/challenge cc_pub_… en el navegador: puede ser pública
POST /api/v2/verify cc_sec_… solo en el servidor: nunca la entregue
GET /api/v2/status cc_sec_… solo en el servidor

La clave va siempre en la cabecera X-CaptchaCore-Key, nunca en la URL.

GET /api/v2/challenge

Solicita un cálculo junto con su política. Parámetro de consulta opcional: form_type (véanse las políticas de formulario). Responde con HTTP 200.

{
  "challenge_id": "ch_a45cd15b0d6d2dc6",
  "nonce": "a45cd15b…1d50",          // einmalig, 5 Minuten gültig
  "expires_at": 1788860066,
  "policy": {
    "mode": "adaptive",
    "challenge_type": "sha256_pow",     // oder argon2id_pow, interaction_step_up
    "challenge_types": ["sha256_pow"],
    "difficulty": 4,                  // führende Nullen im Hash
    "algorithm": "sha256",
    "time_budget_ms": 500,
    "requires_interaction": false,
    "requires_stepup_on_submit": false,
    "memory_hard_enabled": false,
    "form_type": "contact",
    "site_profile": "balanced",
    "uam_level": 0              // 0–3, Under-Attack-Stufe
  },
  "bindings": {                       // bindet den Token an Site, Origin und Formular
    "site_id": 2,
    "origin": "https://example.com",
    "form_type": "contact",
    "issued_at": 1788859766,
    "expires_at": 1788860066,
    "token_ttl_sec": 300
  },
  "bindings_signature": "CBkATjVc…RDg==",  // Ed25519, unveränderlich mitschicken
  "signing_key_id": "k_397d99d39156c5f5",
  "widget": { /* im Admin gepflegtes Erscheinungsbild */ },
  "widget_version": "3.0.0"
}

Importante para clientes propios: bindings y bindings_signature deben volver al token sin modificar. Si faltan o se alteran, /verify responde con block.

POST /api/v2/verify

Comprueba el token del formulario. Por regla general responde con HTTP 200, también en caso de rechazo. Evalúe el cuerpo, no el código de estado.

// Request
{
  "token": "<Wert des Feldes captchacore_token>",
  "form_type": "contact",      // optional, steuert die Policy
  "page_url": "https://…"       // optional, nur für Auswertungen
}

// Response
{
  "valid": true,               // false bei block UND bei step_up
  "decision": "allow",        // allow | challenge | step_up | block
  "action": "allow",          // identisch zu decision, für V1-Kompatibilität
  "request_id": "…",          // für Rückfragen an den Support
  "risk_score": 8,             // 0–100, höher = riskanter
  "confidence": 0.94,          // 0.0–1.0 Signalabdeckung
  "reasons": ["pow_valid", "behavior_human_like"],
  "step_up": null             // bei decision=step_up: Daten für die Zusatzprüfung
}

Cómo tratar decision

decision valid Comportamiento recomendado
allow true Procese el formulario con normalidad.
challenge true Déjelo pasar pero regístrelo. Opcionalmente añada una comprobación propia, como un correo de confirmación.
step_up false NO rechace de plano. El widget realiza la comprobación adicional por sí mismo; devuelva el formulario al usuario con un mensaje amable.
block false Rechace. Muestre un error sin detalles para que los atacantes no aprendan nada.

GET /api/v2/status

Disponibilidad y nivel actual de ataque del sitio. Adecuado para una comprobación de estado en su complemento.

{ "status": "ok", "under_attack": false, "version": "2.x" }

Códigos de estado HTTP

Código Cuerpo Causa y reacción
200 {"valid": …} Caso normal, también en caso de rechazo. Evalúe el cuerpo.
401 {"error":"Missing API key"} Falta la cabecera X-CaptchaCore-Key.
401 {"error":"Invalid API key"} Clave incorrecta, revocada o de otro sitio. También ocurre si se usa la clave pública en /verify.
403 {"error": …} El origen no está entre los dominios permitidos del sitio.
422 {"message": …} Falta un campo obligatorio, normalmente token.
429 Se alcanzó el límite de peticiones. Trátelo como una caída, véase el manejo de errores.
5xx Una avería por nuestra parte. Se aplica su regla fail-open o fail-closed.

Límites de peticiones

Endpoint Límite Contado por
GET /api/v2/challenge60 / minIP del visitante
POST /api/v2/verify600 / minclave de sitio del cliente

Verify se cuenta por sitio, no por IP. Su servidor puede así comprobar tantos formularios como tráfico tenga su sitio, sin bloquearse a sí mismo.

Webhooks

CaptchaCore avisa a sus propios sistemas cuando ocurre algo a lo que quiera reaccionar: cuota alcanzada, modo under-attack activado, cuenta bloqueada o desbloqueada. Se configura en el área de cliente en Sitios → Webhooks (a partir del plan Professional).

Eventos

EventoCuándo
usage.warningCuota mensual al 80 % o 90 %
usage.limit_reachedCuota mensual alcanzada: se rechazan más verificaciones
site.under_attack.activatedModo under-attack de un sitio activado (manual o automáticamente)
site.under_attack.deactivatedModo under-attack de un sitio finalizado
organisation.blockedCuenta bloqueada (p. ej. factura pendiente): se rechazan las llamadas a la API
organisation.unblockedCuenta desbloqueada de nuevo
webhook.testEvento de prueba desde el área de cliente

Qué llega

Un POST con cuerpo JSON. Tres cabeceras ayudan a identificarlo y verificarlo:

POST /ihr-endpunkt HTTP/1.1
Content-Type: application/json
X-CaptchaCore-Event: usage.limit_reached
X-CaptchaCore-Delivery: 5f1c2c1e-…            // eindeutig je Zustellung, gleich bei Wiederholungen
X-CaptchaCore-Timestamp: 1758196800
X-CaptchaCore-Signature: sha256=3b2a…

{
  "event": "usage.limit_reached",
  "occurred_at": "2026-09-18T14:00:00+02:00",
  "organisation_id": 42,
  "data": { "threshold_percent": 100, "used": 10000, "limit": 10000, "plan": "pro", "period": "2026-09" }
}

Verificar la firma

La firma es un HMAC-SHA256 sobre «timestamp.body» con la clave del área de cliente. Compare en tiempo constante y rechace marcas de tiempo de más de cinco minutos: así evita repeticiones de solicitudes interceptadas.

$secret    = getenv('CAPTCHACORE_WEBHOOK_SECRET');
$body      = file_get_contents('php://input');
$timestamp = $_SERVER['HTTP_X_CAPTCHACORE_TIMESTAMP'] ?? '';
$expected  = 'sha256=' . hash_hmac('sha256', $timestamp . '.' . $body, $secret);

if (abs(time() - (int) $timestamp) > 300
    || ! hash_equals($expected, $_SERVER['HTTP_X_CAPTCHACORE_SIGNATURE'] ?? '')) {
    http_response_code(401); exit;
}

$event = json_decode($body, true);
// … verarbeiten, dann schnell mit 2xx antworten
http_response_code(204);

Entrega y reintentos

  • Responda con un estado 2xx en menos de 5 segundos; procese lo costoso de forma asíncrona.
  • Ante errores o tiempo de espera se reintenta hasta tres veces (a los 30 segundos y a los 5 minutos) con el mismo ID de entrega; haga su procesamiento idempotente con él.
  • Tras diez fallos consecutivos el webhook se desactiva; en el área de cliente verá el motivo y podrá reactivarlo.
  • Cada entrega, con código de respuesta, duración y extracto de la respuesta, figura en «Entregas», también para depurar en su lado.

Versionado y estabilidad

Quien mantiene un complemento necesita saber con qué puede contar. Estos son nuestros compromisos.

La versión de la ruta se mantiene estable

Dentro de /api/v2 no eliminamos ni renombramos campos de respuesta. Pueden aparecer campos nuevos en cualquier momento: lea las respuestas con tolerancia y no falle ante claves desconocidas.

V1 sigue disponible por ahora

La interfaz antigua en /api/v1 se sigue atendiendo, pero ya no recibe señales nuevas. Las integraciones nuevas deberían usar exclusivamente V2.

El widget se actualiza solo

A través de la ruta del CDN captchacore-v2.min.js siempre obtiene la versión mantenida. No incluya una copia propia o se perderá las mejoras en la detección de bots.

Anunciamos los cambios

Todo lo que pueda afectar a integraciones existentes se anuncia con antelación en el estado público del sistema y por correo a la dirección técnica registrada.