CashoutGuard

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.

CampoTipoObligatorioDescripción
eventstringObligatoriosignup, login, offer_click, conversion, cashout o custom.
account_idstringObligatorioTu ID de usuario, máximo 191 caracteres. Usa un ID opaco, no un email.
request_idstringOpcionalViene de collect() en el navegador. Válido durante 1 hora. Sin él solo tenemos datos de red.
ipstringOpcionalLa IP del usuario. Solo hace falta cuando no tienes request_id, por ejemplo en los postbacks.
emailstringOpcionalSe guarda como hash. Detecta dominios desechables y emails compartidos entre cuentas.
payout_addressstringOpcionalEmail de PayPal, wallet o cuenta a la que va el dinero. Se guarda como hash.
payout_methodstringOpcionalTexto libre, como paypal, usdt o giftcard.
amountnumberOpcionalMonto del retiro o de la conversión.
currencystringOpcionalCódigo de moneda, por ejemplo USD. USD por defecto en los retiros.
offer_idstringOpcionalID de la oferta, para offer_click y conversion. Se usa para la velocidad de ofertas.
offer_countrystringOpcionalPaís por el que paga la oferta (código ISO, p. ej. US), en conversiones. Un usuario en otro país se marca.
geo_countrystringOpcionalTu 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_idstringOpcionalID de transacción de la red para una conversión. Se usa para detectar duplicados.
offer_namestringOpcionalTítulo de la oferta tal como lo ven tus usuarios. Se muestra en todo el panel en lugar del id.
click_idstringOpcionalTu id de clic. Envía el mismo valor en offer_click y en la conversión para vincularlos.
sourcestringOpcionalDe dónde viene el evento, por ejemplo el nombre de la offerwall o de la red.
metaobjectOpcionalHasta 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.

  • score va de 0 a 100. Los motivos se combinan para que varias señales medias sumen sin pasar de 100.
  • reasons enumera 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.confidence indica qué tan único parece el dispositivo. Los fingerprints se pueden falsificar, así que nunca afirmamos certeza.
  • linked_accounts enumera otras cuentas que comparten dispositivo, dirección de pago o email con esta.
  • cashout solo 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ónPuntuación (por defecto)action en modo sombraaction en modo activoQué hacer
allow0-49allowallowContinúa con normalidad.
review50-79allowreviewRetén el retiro o pide una verificación adicional. Aparece en tu cola de revisión.
block80-100allowblockDeté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.

EventoCuándo enviarloQué detecta
signupCuenta creadaMulticuentas, bots, registros con VPN, emails desechables
loginInicio de sesión correctoCuentas compartidas, cambios de país, dispositivos nuevos
offer_clickEl usuario abre una ofertaInicia el cronómetro de la velocidad de ofertas
conversionPostback del offerwall o de la redOfertas completadas demasiado rápido, transacciones duplicadas, ráfagas de conversiones
cashoutEl usuario solicita un retiroDirecciones de pago compartidas, velocidad de retiros, retiros de cuentas nuevas
customCualquier otra cosa que quieras puntuarSeñ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ódigoSignificadoGrupoPeso
ip_torNodo de salida de TorRed60
ip_vpnConexión VPNRed45
ip_datacenterIP de datacenter / hostingRed40
timezone_mismatchLa zona horaria del navegador no coincide con la ubicación de la IPRed20
webrtc_ip_leakIP real filtrada en otro paísRed30
ip_multi_timezoneLa misma IP con muchas zonas horarias distintas (salida de proxy)Red50
proxy_farm_ipVarias cuentas en una IP, todas con el mismo reloj extranjero (proxy compartido)Red60
provider_rotationMuchos proveedores de internet en pocos días, con reloj extranjero (proxy residencial rotativo)Red45
privacy_relayiCloud Private Relay (función de privacidad)Red5
proxy_headersLa petición llegó a través de un proxyRed20
geo_sources_disagreeLas fuentes de geolocalización no coincidenRed45
ip_shared_with_accountsMisma IP usada hoy por otras cuentasRed25
country_changedCambio de paísRed15
impossible_travelViaje imposible: IPs de dos países lejanos en pocas horasRed50
locale_country_mismatchIdioma del navegador de otro paísRed30
phone_country_mismatchTeléfono de otro país que la IPRed20
ip_flagged_on_networkIP bloqueada en otras websRed25
device_shared_with_accountsDispositivo compartido con otras cuentasDispositivo55
automation_detectedBot o herramienta de automatizaciónDispositivo70
auto_clickerAuto-clicker en el móvilDispositivo55
scripted_behaviorUso de la página por scriptDispositivo45
emulator_or_vmEmulador o máquina virtualDispositivo45
browser_tamperingNavegador manipulado / suplantadoDispositivo35
device_rootedMóvil rooteadoDispositivo25
incognitoNavegación privadaDispositivo10
device_spoofedModelo de móvil falsoDispositivo55
emulator_profilePerfil por defecto de emuladorDispositivo60
cloud_phoneTeléfono en la nubeDispositivo60
phone_farm_displayPantalla de granja de móvilesDispositivo45
device_market_mismatchMóvil solo de China en el extranjeroDispositivo25
storage_cleared_reappearedBorró las cookies y volvióDispositivo15
no_device_dataNo se enviaron datos del dispositivoDispositivo15
request_reusedToken de dispositivo reutilizado por otra cuentaDispositivo40
device_farm_proxyGranja de teléfonos con proxies residencialesDispositivo85
device_clonesClones de dispositivo: un teléfono que reinicia su identidad para hacerse pasar por muchas cuentas nuevas en una redDispositivo55
payout_address_sharedDirección de pago usada por otras cuentasCuenta70
email_disposableEmail desechableCuenta25
email_invalidEmail que no puede recibir correo (el dominio no existe para el correo)Cuenta35
email_sharedMismo email que otra cuentaCuenta50
phone_sharedTeléfono compartido con otras cuentasCuenta50
phone_voipTeléfono VoIP o virtualCuenta40
phone_invalidTeléfono no válidoCuenta30
many_devices_per_accountDemasiados dispositivos en una cuentaCuenta20
flagged_on_networkMarcado en otras webs protegidas por CashoutGuardCuenta45
face_same_personMisma cara que otras cuentasCuenta0
offer_completed_too_fastOferta completada imposiblemente rápidoComportamiento40
slow_after_clickConvirtió mucho después del clicComportamiento0
conversion_velocityDemasiadas conversiones por horaComportamiento30
conversion_without_clickConversión sin clic desde tu offerwallComportamiento35
click_conversion_country_mismatchClic en un país, conversión en otroComportamiento30
offer_country_mismatchEl país de la oferta no es el del usuarioComportamiento45
click_velocityDemasiados clics en ofertas en pocos minutosComportamiento35
earnings_spikeGanancias muy por encima de un usuario normalComportamiento30
source_high_reversalsFuente con muchos chargebacksComportamiento45
offer_high_reversalsOferta con muchos chargebacksComportamiento35
duplicate_transactionID de transacción duplicadoComportamiento60
offer_repeated_other_publisherLa misma oferta ya hecha a través de otro publisherComportamiento85
offer_repeated_other_accountMisma oferta ya hecha por otra cuenta en este dispositivoComportamiento85
offer_farm_burstOleada de granja: muchas cuentas nuevas con dispositivos de granja en esta oferta en pocas horasComportamiento30
signup_burstRáfaga de registros: varias cuentas se registraron con segundos de diferencia desde IPs distintasComportamiento25
face_check_requiredFalta la comprobación facialComportamiento0
face_check_failedComprobación facial fallidaComportamiento0
publisher_farm_waveOleada 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 emuladoresComportamiento55
cashout_velocityDemasiados retiros por díaComportamiento30
new_account_cashoutRetiro desde una cuenta recién creadaComportamiento20
blocklistedEn tu lista negraTus listas100

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:

Errores y límites

HTTPCuerpoSignificado
401missing_or_invalid_secret_keyClave 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".
404not_foundCuenta no encontrada (endpoints de cuenta).
422{ "message", "errors" }Cuerpo no válido. errors enumera cada campo.
429Too Many RequestsSe alcanzó el límite de solicitudes. La API del servidor permite 3.000 solicitudes por minuto por sitio.
401 · agent.jsunknown_public_keyEl data-key no es la clave pública de ningún sitio. Vuelve a copiar pk_live_... desde Instalación.
403 · agent.jsorigin_not_allowedEl 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.
403account_suspendedLa cuenta está suspendida, normalmente por una factura sin pagar. Revisa Facturación.
503temporarily_unavailableCorte 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.