Documentación
Un script en el navegador y una llamada a la API desde tu servidor. Todo lo que necesitas para salir en vivo, en una sola página.
Inicio rápido
Necesitas tu clave pública (pk_...) para el navegador y tu clave secreta (sk_...) para tu servidor. Ambas están en el panel, en Sitios y claves.
1. Agrega el script
Pon esta etiqueta en las páginas donde los usuarios se registran, inician sesión, hacen clic en ofertas o solicitan un retiro. Se inicializa sola a partir de data-key
<script src="https://cashoutguard.com/v1/agent.js" data-key="pk_live_YOUR_PUBLIC_KEY" async></script>
2. Recolecta y envía el request_id a tu servidor
Llama a collect() en el momento que importa, con el nombre del evento. Se resuelve con { request_id, visitor_id }
const { request_id } = await CashoutGuard.collect({ event: 'cashout' });
collect() nunca se rechaza. Si falla (bloqueado, sin conexión, tiempo agotado) se resuelve con request_id: null y un texto de error. Envía el formulario de todos modos y deja que tu servidor decida.
3. Pide una decisión desde tu servidor
Envía el evento por POST a /v1/evaluate con tu clave secreta en el header Authorization: Bearer sk_live_...
Nunca pongas la clave secreta en el código del navegador ni en una app móvil. Solo tu servidor debe llamar a /v1/evaluate.
Solicitud evaluate
POST https://cashoutguard.com/v1/evaluate puntúa un evento de una cuenta, lo vincula con su dispositivo, red y dirección de pago, y devuelve una decisión. Envía JSON.
| Campo | Tipo | Obligatorio | Descripción |
|---|---|---|---|
event | string | Obligatorio | signup, login, offer_click, conversion, cashout o custom. |
account_id | string | Obligatorio | Tu ID de usuario, máximo 191 caracteres. Usa un ID opaco, no un email. |
request_id | string | Opcional | Viene de collect() en el navegador. Válido durante 1 hora. Sin él solo tenemos datos de red. |
ip | string | Opcional | La IP del usuario. Solo hace falta cuando no tienes request_id, por ejemplo en los postbacks. |
email | string | Opcional | Se guarda como hash. Detecta dominios desechables y emails compartidos entre cuentas. |
payout_address | string | Opcional | Email de PayPal, wallet o cuenta a la que va el dinero. Se guarda como hash. |
payout_method | string | Opcional | Texto libre, como paypal, usdt o giftcard. |
amount | number | Opcional | Monto del retiro o de la conversión. |
currency | string | Opcional | Código de moneda, por ejemplo USD. USD por defecto en los retiros. |
offer_id | string | Opcional | ID de la oferta, para offer_click y conversion. Se usa para la velocidad de ofertas. |
offer_country | string | Opcional | País por el que paga la oferta (código ISO, p. ej. US), en conversiones. Un usuario en otro país se marca. |
geo_country | string | Opcional | Tu propia geolocalización de la IP del usuario (código ISO). Si no coincide con la nuestra, la IP probablemente es una salida de proxy. |
transaction_id | string | Opcional | ID de transacción de la red para una conversión. Se usa para detectar duplicados. |
offer_name | string | Opcional | Título de la oferta tal como lo ven tus usuarios. Se muestra en todo el panel en lugar del id. |
click_id | string | Opcional | Tu id de clic. Envía el mismo valor en offer_click y en la conversión para vincularlos. |
source | string | Opcional | De dónde viene el evento, por ejemplo el nombre de la offerwall o de la red. |
meta | object | Opcional | Hasta 20 campos de texto extra guardados con el evento (por ejemplo sub ids). |
Respuesta
La respuesta siempre tiene la misma estructura. Los campos que no aplican son null.
scoreva de 0 a 100. Los motivos se combinan para que varias señales medias sumen sin pasar de 100.reasonsenumera cada señal que se activó, de la más fuerte a la más débil, con su peso, detalles y una etiqueta en inglés claro que puedes mostrar tal cual.device.confidenceindica qué tan único parece el dispositivo. Los fingerprints se pueden falsificar, así que nunca afirmamos certeza.linked_accountsenumera otras cuentas que comparten dispositivo, dirección de pago o email con esta.cashoutsolo aparece en los eventos cashout e indica el estado en tu cola de revisión.
Decisiones y acciones
Actúa según el campo action, no según decision. Los umbrales de abajo son los valores por defecto y se pueden cambiar por sitio en el panel.
| Decisión | Puntuación (por defecto) | action en modo sombra | action en modo activo | Qué hacer |
|---|---|---|---|---|
allow | 0-49 | allow | allow | Continúa con normalidad. |
review | 50-79 | allow | review | Retén el retiro o pide una verificación adicional. Aparece en tu cola de revisión. |
block | 80-100 | allow | block | Detén la acción. Congela el saldo o rechaza el registro. |
En modo sombra action siempre es allow, así que puedes publicar el mismo código de forma segura antes de activarlo. Cuando cambies a modo activo, no cambia nada en tu código.
Tipos de evento
Envía los eventos que tengas. Signup y cashout son los más importantes; las conversiones alimentan las comprobaciones de velocidad de ofertas.
| Evento | Cuándo enviarlo | Qué detecta |
|---|---|---|
signup | Cuenta creada | Multicuentas, bots, registros con VPN, emails desechables |
login | Inicio de sesión correcto | Cuentas compartidas, cambios de país, dispositivos nuevos |
offer_click | El usuario abre una oferta | Inicia el cronómetro de la velocidad de ofertas |
conversion | Postback del offerwall o de la red | Ofertas completadas demasiado rápido, transacciones duplicadas, ráfagas de conversiones |
cashout | El usuario solicita un retiro | Direcciones de pago compartidas, velocidad de retiros, retiros de cuentas nuevas |
custom | Cualquier otra cosa que quieras puntuar | Señales del dispositivo y de la red |
Offerwalls y postbacks
Las conversiones de offerwall llegan por postback, de servidor a servidor, sin navegador. CashoutGuard vincula cada conversión con el clic que la originó, así que se puntúa con el dispositivo y la red de ese clic.
1. Cuando el usuario abre una oferta
Recoge en el navegador y envía un offer_click con la oferta y tu id de clic.
2. Cuando llega el postback
Envía una conversion desde tu manejador de postback con el mismo account_id y click_id. No hace falta request_id. Pasa la IP del usuario si la offerwall te la da.
En las conversiones se comprueba: completada demasiado rápido para esa oferta, conversión sin clic, clic y conversión desde países distintos, ganancias muy por encima del día habitual de la cuenta e ids de transacción duplicados.
Redes de offerwall
Si gestionas una red con muchos editores, envía ids de cuenta que incluyan el editor y activa el "Modo red de offerwall" en Sitios. Así las cuentas solo se vinculan dentro del mismo editor, porque es normal que una persona use varias webs de tus editores. Formato: publisher_id:user_id
Catálogo de ofertas
POST /v1/offers envía los nombres de tus ofertas (hasta 1.000 por llamada). Los eventos pasados y futuros mostrarán el nombre real aunque solo lleven el id.
Códigos de motivo
Estos códigos aparecen en reasons[].code. Muestra el texto a tus moderadores; usa el código en tu lógica.
| Código | Significado | Grupo | Peso |
|---|---|---|---|
ip_tor | Nodo de salida de Tor | Red | 60 |
ip_vpn | Conexión VPN | Red | 45 |
ip_datacenter | IP de datacenter / hosting | Red | 40 |
timezone_mismatch | La zona horaria del navegador no coincide con la ubicación de la IP | Red | 20 |
webrtc_ip_leak | IP real filtrada en otro país | Red | 30 |
ip_multi_timezone | La misma IP con muchas zonas horarias distintas (salida de proxy) | Red | 50 |
proxy_farm_ip | Varias cuentas en una IP, todas con el mismo reloj extranjero (proxy compartido) | Red | 60 |
provider_rotation | Muchos proveedores de internet en pocos días, con reloj extranjero (proxy residencial rotativo) | Red | 45 |
privacy_relay | iCloud Private Relay (función de privacidad) | Red | 5 |
proxy_headers | La petición llegó a través de un proxy | Red | 20 |
geo_sources_disagree | Las fuentes de geolocalización no coinciden | Red | 45 |
ip_shared_with_accounts | Misma IP usada hoy por otras cuentas | Red | 25 |
country_changed | Cambio de país | Red | 15 |
impossible_travel | Viaje imposible: IPs de dos países lejanos en pocas horas | Red | 50 |
locale_country_mismatch | Idioma del navegador de otro país | Red | 30 |
phone_country_mismatch | Teléfono de otro país que la IP | Red | 20 |
ip_flagged_on_network | IP bloqueada en otras webs | Red | 25 |
device_shared_with_accounts | Dispositivo compartido con otras cuentas | Dispositivo | 55 |
automation_detected | Bot o herramienta de automatización | Dispositivo | 70 |
auto_clicker | Auto-clicker en el móvil | Dispositivo | 55 |
scripted_behavior | Uso de la página por script | Dispositivo | 45 |
emulator_or_vm | Emulador o máquina virtual | Dispositivo | 45 |
browser_tampering | Navegador manipulado / suplantado | Dispositivo | 35 |
device_rooted | Móvil rooteado | Dispositivo | 25 |
incognito | Navegación privada | Dispositivo | 10 |
device_spoofed | Modelo de móvil falso | Dispositivo | 55 |
emulator_profile | Perfil por defecto de emulador | Dispositivo | 60 |
cloud_phone | Teléfono en la nube | Dispositivo | 60 |
phone_farm_display | Pantalla de granja de móviles | Dispositivo | 45 |
device_market_mismatch | Móvil solo de China en el extranjero | Dispositivo | 25 |
storage_cleared_reappeared | Borró las cookies y volvió | Dispositivo | 15 |
no_device_data | No se enviaron datos del dispositivo | Dispositivo | 15 |
request_reused | Token de dispositivo reutilizado por otra cuenta | Dispositivo | 40 |
device_farm_proxy | Granja de teléfonos con proxies residenciales | Dispositivo | 85 |
device_clones | Clones de dispositivo: un teléfono que reinicia su identidad para hacerse pasar por muchas cuentas nuevas en una red | Dispositivo | 55 |
payout_address_shared | Dirección de pago usada por otras cuentas | Cuenta | 70 |
email_disposable | Email desechable | Cuenta | 25 |
email_invalid | Email que no puede recibir correo (el dominio no existe para el correo) | Cuenta | 35 |
email_shared | Mismo email que otra cuenta | Cuenta | 50 |
phone_shared | Teléfono compartido con otras cuentas | Cuenta | 50 |
phone_voip | Teléfono VoIP o virtual | Cuenta | 40 |
phone_invalid | Teléfono no válido | Cuenta | 30 |
many_devices_per_account | Demasiados dispositivos en una cuenta | Cuenta | 20 |
flagged_on_network | Marcado en otras webs protegidas por CashoutGuard | Cuenta | 45 |
face_same_person | Misma cara que otras cuentas | Cuenta | 0 |
offer_completed_too_fast | Oferta completada imposiblemente rápido | Comportamiento | 40 |
slow_after_click | Convirtió mucho después del clic | Comportamiento | 0 |
conversion_velocity | Demasiadas conversiones por hora | Comportamiento | 30 |
conversion_without_click | Conversión sin clic desde tu offerwall | Comportamiento | 35 |
click_conversion_country_mismatch | Clic en un país, conversión en otro | Comportamiento | 30 |
offer_country_mismatch | El país de la oferta no es el del usuario | Comportamiento | 45 |
click_velocity | Demasiados clics en ofertas en pocos minutos | Comportamiento | 35 |
earnings_spike | Ganancias muy por encima de un usuario normal | Comportamiento | 30 |
source_high_reversals | Fuente con muchos chargebacks | Comportamiento | 45 |
offer_high_reversals | Oferta con muchos chargebacks | Comportamiento | 35 |
duplicate_transaction | ID de transacción duplicado | Comportamiento | 60 |
offer_repeated_other_publisher | La misma oferta ya hecha a través de otro publisher | Comportamiento | 85 |
offer_repeated_other_account | Misma oferta ya hecha por otra cuenta en este dispositivo | Comportamiento | 85 |
offer_farm_burst | Oleada de granja: muchas cuentas nuevas con dispositivos de granja en esta oferta en pocas horas | Comportamiento | 30 |
signup_burst | Ráfaga de registros: varias cuentas se registraron con segundos de diferencia desde IPs distintas | Comportamiento | 25 |
face_check_required | Falta la comprobación facial | Comportamiento | 0 |
face_check_failed | Comprobación facial fallida | Comportamiento | 0 |
publisher_farm_wave | Oleada de granja de su publisher: la mayoría de cuentas nuevas que envió a esta oferta venían de móviles en la nube o emuladores | Comportamiento | 55 |
cashout_velocity | Demasiados retiros por día | Comportamiento | 30 |
new_account_cashout | Retiro desde una cuenta recién creada | Comportamiento | 20 |
blocklisted | En tu lista negra | Tus listas | 100 |
Los pesos se combinan con un noisy-OR: score = 100 x (1 - producto de (1 - weight/100)). Dos señales de 40 dan 64, no 80. Una coincidencia con la lista negra siempre da 100.
Webhooks
Configura una URL de webhook en el panel. Enviamos un POST por cada evento puntuado como block (event.blocked), cuando una cuenta pasa a block (account.blocked) y cuando tu equipo decide un retiro (cashout.decided). Ignora los tipos que no uses.
Cada solicitud se firma con tu secreto de webhook. El HMAC-SHA256 en hexadecimal del cuerpo sin procesar va en el header X-CashoutGuard-Signature
Responde con cualquier estado 2xx. Las entregas fallidas se reintentan hasta 5 veces: a los 10 segundos, 1 minuto, 5 minutos y 15 minutos.
Estado de la cuenta
GET /v1/accounts/{account_id} devuelve la vista de riesgo actual de una cuenta sin crear un evento.
POST /v1/accounts/{account_id}/status permite que tus propias herramientas de moderación cambien el estado de una cuenta.
active: Puntuación normal.allowlisted: Las puntuaciones se limitan por debajo del umbral de revisión. Para usuarios de confianza y personal.blocked: Cada evento devuelve block con puntuación 100.
Reversiones y precisión
POST /v1/outcomes informa qué pasó con las conversiones después de puntuarlas: revertidas por el offerwall o el anunciante (chargeback) o confirmadas como válidas. Cruzamos tus ids con las conversiones y medimos el motor con tu propio tráfico en la página Precisión: cuánto dinero revertido habíamos marcado y qué señales predicen las reversiones.
Sin código: sube en la página Precisión el export mensual de reversiones de tus offerwalls (CSV o Excel). Encontramos la columna de ids automáticamente.
PHP, Node.js, Python, Android
No necesitas SDK. Basta con una llamada HTTP simple con un timeout corto. Ejemplo completo de retiro:
- cashoutguard.php: PHP 7.4+, un solo archivo, sin Composer. Haz require y listo.
- composer require cashoutguard/cashoutguard-php: el mismo cliente PHP con su README, para tu carpeta vendor.
- npm install @cashoutguard/node: Node 18+, con tipos de TypeScript.
- cashoutguard-wordpress.zip: plugin de WordPress: el script del navegador en todas las páginas, registros e inicios de sesión puntuados, y hooks para retiros, conversiones de offerwall y reversiones.
Errores y límites
| HTTP | Cuerpo | Significado |
|---|---|---|
| 401 | missing_or_invalid_secret_key | Clave secreta ausente, incorrecta o revocada. La respuesta indica el motivo en "reason" (missing, public_key, unknown_secret, placeholder, malformed) y cómo arreglarlo en "hint". |
| 404 | not_found | Cuenta no encontrada (endpoints de cuenta). |
| 422 | { "message", "errors" } | Cuerpo no válido. errors enumera cada campo. |
| 429 | Too Many Requests | Se alcanzó el límite de solicitudes. La API del servidor permite 3.000 solicitudes por minuto por sitio. |
| 401 · agent.js | unknown_public_key | El data-key no es la clave pública de ningún sitio. Vuelve a copiar pk_live_... desde Instalación. |
| 403 · agent.js | origin_not_allowed | El dominio de la página no está en los orígenes permitidos del sitio. Añádelo en Sitios o deja la lista vacía. localhost siempre funciona. agent.js también lo explica en la consola del navegador. Los WebViews de apps envían el origen "null": añade null a la lista para aceptarlos. El SDK nativo de Android no necesita nada. |
| 403 | account_suspended | La cuenta está suspendida, normalmente por una factura sin pagar. Revisa Facturación. |
| 503 | temporarily_unavailable | Corte breve por nuestra parte. Deja pasar la acción y reintenta más tarde. |
Fail open: si la llamada agota el tiempo o da error, permite la acción y regístrala. Nunca dejes que una caída nuestra frene a tus usuarios.