Documentación
El sitio funciona sobre una API pública con dieciocho endpoints instantáneos. Cada uno está documentado aquí: qué hace, sus límites y un ejemplo de solicitud/respuesta. Sin cuenta; los resultados instantáneos no se almacenan. La APIs de gestión al final de esta página (monitorización de uptime, páginas de estado, enlaces cortos) son la excepción con estado: persisten bajo sus propias claves de gestión.
Códigos de estado
- 301 — Redirección permanente
Redirección permanente. Los buscadores transfieren las señales de posicionamiento al destino.
- 302 — Redirección temporal
Redirección temporal. La URL original sigue siendo la canónica.
- 307 — Redirección temporal
Redirección temporal que conserva el método y el cuerpo de la solicitud.
- 308 — Redirección permanente
Redirección permanente que conserva el método y el cuerpo de la solicitud.
- 200 — Éxito
Destino final alcanzado. La cadena termina aquí.
- 404 — Error de cliente
El destino no existe — un objetivo de redirección roto.
- 500 — Error de servidor
El destino se resolvió pero el servidor no respondió correctamente.
Resultados clasificados, no errores
En los cuatro endpoints instantáneos, los destinos bloqueados, los dominios irresolubles y las URL inválidas no son errores: cada solicitud bien formada devuelve HTTP 200 con un estado que clasifica el resultado. Solo las solicitudes malformadas, los cuerpos demasiado grandes y la limitación de tasa producen respuestas de error.
Límites compartidos
- Protocolos
- http, https
- Método
- GET
- Límites de tasa
- 30 solicitudes/minuto y 500/día por IP — compartidos por todos los endpoints
- Cuerpo de la solicitud
- hasta 10 KiB, JSON estricto
Directorio de servicios e informes de la comunidad (Phase 14)
Un directorio público y curado de servicios en línea populares con informes comunitarios anónimos. Las lecturas son públicas y están cacheadas; el POST de informes es la excepción con estado — persiste y lleva límites de frecuencia, deduplicación y moderación. La señal comunitaria y la comprobación automática son siempre indicadores separados; nada aquí afirma una caída confirmada.
GET /api/v1/catalog/services?sort=reports_24h&page=1&page_size=20
→ 200
{ "items": [ { "slug": "chatgpt", "name": "ChatGPT",
"category": { "slug": "ai-assistants", "name": "AI Assistants" },
"community": { "signal": "reports_increasing", "reports_15m": 3,
"reports_1h": 11, "reports_24h": 42,
"last_report_at": "2026-10-20T09:58:12Z" },
"automated_check": { "state": "operational",
"checked_at": "2026-10-20T09:55:00Z" } } ],
"page": 1, "page_size": 20, "total": 1 }
POST /api/v1/catalog/services/chatgpt/reports
{ "problem_type": "site_unreachable", "comment": "Login loop on mobile" }
→ 201
{ "service_slug": "chatgpt", "problem_type": "site_unreachable",
"comment": "Login loop on mobile", "created_at": "2026-10-20T10:01:33Z" }
Reads are public and cached (ETag + max-age=30); unknown/draft/hidden
slugs all answer 404 (no existence oracle); merged slugs 301 to the
target. Reports are anonymous: no account, no network fetch on
submission, rate limited per IP, comments plain text (≤500 chars).
Community signal and the automated check are always separate blocks —
no value on this API means "confirmed down".- Paginación
- page ≥1, page_size por defecto 20, máx 100; orden determinista (desempate por slug)
- Límites de informes
- dedup 1 por servicio cada 10 min por IP, luego 10/h y 30/día por IP; 429 + Retry-After; fail-closed
- Moderación y privacidad
- política auto-publish o hold-all; interruptores de apagado; solo se almacena un hash seudónimo con rotación diaria — nunca IP cruda ni user-agent
Redirect Checker — POST /api/v1/check
Sigue la cadena completa de redirecciones y almacena el resultado.
POST /api/v1/check
Content-Type: application/json
{ "url": "https://bit.ly/example" }
{
"id": "9pr2kxq4",
"status": "completed",
"input_url": "https://bit.ly/example",
"final_url": "https://example.com/product",
"final_domain": "example.com",
"final_status": 200,
"redirect_count": 3,
"duration_ms": 482,
"redirects": [
{ "sequence": 0, "requested_url": "https://bit.ly/example",
"status_code": 301, "next_url": "https://tracker.example/click",
"duration_ms": 83 },
{ "sequence": 3, "requested_url": "https://example.com/product",
"status_code": 200, "duration_ms": 184 }
],
"errors": null
}
GET /api/v1/check/{id} — obtiene un resultado guardado por su ID público.- Redirecciones máximas
- 20 saltos
- Tiempo de espera
- 15 segundos
GET /api/v1/check/{id} — obtiene un resultado guardado por su ID público.
Uptime Checker — POST /api/v1/uptime-check
Comprueba si un sitio responde. El resultado se clasifica: UP para una respuesta correcta, HTTP_ERROR si el sitio responde con 4xx/5xx, más TIMEOUT, DNS_ERROR, TLS_ERROR, CONNECTION_ERROR y BLOCKED_TARGET.
POST /api/v1/uptime-check
Content-Type: application/json
{ "url": "https://example.com" }
{
"url": "https://example.com",
"status": "UP",
"reachable": true,
"httpStatus": 200,
"httpStatusText": "OK",
"responseTimeMs": 184,
"finalUrl": "https://example.com/",
"redirectCount": 1,
"tls": { "enabled": true, "valid": true }
}- Redirecciones máximas
- 10 saltos
- Tiempo de espera
- 10 segundos
HTTP Status Checker — POST /api/v1/http-status-check
Devuelve el código de estado final de una URL, las cabeceras de la respuesta final, un resumen compacto de la cadena de redirecciones y el estado TLS.
POST /api/v1/http-status-check
Content-Type: application/json
{ "url": "https://example.com" }
{
"url": "https://example.com",
"status": "UP",
"reachable": true,
"httpStatus": 200,
"httpStatusText": "OK",
"finalUrl": "https://example.com/",
"redirectCount": 1,
"headers": {
"Content-Type": ["text/html; charset=utf-8"],
"Server": ["nginx"]
},
"headers_analysis": {
"security_missing": ["Strict-Transport-Security", "Content-Security-Policy"],
"caching": { "Cache-Control": "max-age=604800" }
},
"tls": { "enabled": true, "valid": true }
}- Redirecciones máximas
- 10 saltos
- Tiempo de espera
- 10 segundos
Response Time Checker — POST /api/v1/response-time-check
Mide de 1 a 5 muestras y clasifica un veredicto de velocidad a partir de la mediana del time to first byte. Devuelve la duración de cada muestra, mín/mediana/máx/media y el desglose de tiempos del último salto.
POST /api/v1/response-time-check
Content-Type: application/json
{ "url": "https://example.com", "samples": 3 }
{
"url": "https://example.com",
"samples": 3,
"status": "UP",
"verdict": "FAST",
"completed": 3,
"timesMs": [184, 201, 176],
"stats": { "minMs": 176, "medianMs": 184, "maxMs": 201, "meanMs": 187 },
"breakdown": { "dnsMs": 12, "connectMs": 23, "tlsMs": 45, "ttfbMs": 184, "totalMs": 190 },
"httpStatus": 200,
"httpStatusText": "OK",
"finalUrl": "https://example.com/",
"redirectCount": 1,
"tls": { "enabled": true, "valid": true }
}- Muestras
- 1–5 (predeterminado 3)
- Tiempo de espera por muestra
- 10 segundos
- Límite de la serie
- 30 segundos
SSL Checker — POST /api/v1/ssl-check
Inspecciona el certificado TLS que presenta un host: validez, emisor, SAN, protocolo y cifrado negociados, y la cadena. Sonda solo TLS: la página nunca se descarga; un certificado defectuoso se notifica con un motivo concreto.
POST /api/v1/ssl-check
Content-Type: application/json
{ "url": "https://example.com" }
{
"url": "https://example.com",
"host": "example.com",
"status": "UP",
"reachable": true,
"handshakeMs": 45,
"tls": {
"version": "TLS 1.3",
"cipher": "TLS_AES_256_GCM_SHA384",
"verified": true,
"status": "valid",
"daysRemaining": 89,
"notBefore": "2026-08-01T00:00:00Z",
"notAfter": "2026-10-30T00:00:00Z",
"subject": "CN=example.com",
"issuer": "CN=R3,O=Let's Encrypt,C=US",
"sans": ["example.com", "www.example.com"],
"chain": ["CN=example.com", "CN=R3,O=Let's Encrypt,C=US"]
}
}- Puerto
- el de la URL (443 por defecto)
- Tiempo de espera de la sonda
- 10 segundos
DNS Checker — POST /api/v1/dns-check
Consulta los registros A, AAAA, CNAME, MX, TXT y NS de un host. Solo consulta e informe: no se realizan conexiones y cada dirección resuelta se etiqueta como pública o no pública.
POST /api/v1/dns-check
Content-Type: application/json
{ "url": "example.com" }
{
"url": "example.com",
"host": "example.com",
"status": "UP",
"durationMs": 38,
"records": {
"a": ["93.184.216.34"],
"aaaa": ["2606:2800:220:1:248:1893:25c8:1946"],
"cname": [],
"mx": [{ "host": "mail.example.com", "pref": 10 }],
"txt": ["v=spf1 include:_spf.example.com ~all"],
"ns": ["ns1.example.com", "ns2.example.com"]
},
"addressLabels": [
{ "address": "93.184.216.34", "classification": "public", "reason": "public" }
]
}- Familias de registros
- A, AAAA, CNAME, MX, TXT, NS
- Tiempo de espera de consulta
- 10 segundos en total, 3 segundos por familia
Comprobador de caducidad de dominio — POST /api/v1/domain-expiry-check
Responde cuándo caduca un dominio vía el flujo RDAP fijo de rdap.org: veredicto, días restantes, fechas de registro/caducidad y estados del registro. La primera herramienta del repositorio que consulta a un tercero fijo: solo HTTPS en cada salto, sin proxy, redirecciones y tamaño limitados; el dominio consultado nunca es contactado. 404 se convierte en veredicto not_found (no registrado / registro sin RDAP / nada publicado), 429 y 5xx en UNKNOWN_ERROR honesto.
POST /api/v1/domain-expiry-check
Content-Type: application/json
{ "url": "example.com" }
{
"url": "example.com",
"host": "example.com",
"status": "UP",
"verdict": "ok",
"daysRemaining": 214,
"registration": "2019-01-15T05:00:00Z",
"expiration": "2027-04-15T04:00:00Z",
"lastChanged": "2026-08-01T11:20:00Z",
"registryStatuses": ["ok"],
"source": "rdap",
"durationMs": 420
}- Veredictos
- ok, expiring (≤30 días), expired, not_found, unknown
- Tiempo límite de consulta
- 10 segundos en total (DOMAIN_EXPIRY_TIMEOUT)
- Conexión saliente
- solo rdap.org; HTTPS en cada salto; proxy desactivado; límite de 10 redirecciones; tope de 1 MiB; el dominio nunca es contactado
Consulta WHOIS — POST /api/v1/whois-check
Responde qué sabe el registro sobre un dominio: registrador, nameservers, fechas e indicadores de estado. RDAP primero vía rdap.org, con respaldo del WHOIS clásico (TCP 43 en bruto) para zonas sin RDAP — la primera herramienta del repositorio con TCP en bruto: cada reenvío debe ser un objetivo host[:43], se resuelve por el pipeline SSRF compartido y se conecta a la dirección pública fijada; límite de 2 reenvíos, tope de 64 KiB; el dominio consultado nunca es contactado. Un 404 de RDAP activa el respaldo; una respuesta definitiva de inexistencia aparece como notFound.
POST /api/v1/whois-check
Content-Type: application/json
{ "url": "mail.ru" }
{
"url": "mail.ru",
"host": "mail.ru",
"status": "UP",
"source": "whois",
"registrar": "VK-TECH",
"nameservers": ["ns1.mail.ru", "ns2.mail.ru"],
"registration": "1997-10-01T00:00:00Z",
"expiration": "2026-10-01T00:00:00Z",
"statuses": ["REGISTERED, DELEGATED, VERIFIED"],
"durationMs": 640
}- Fuentes
- RDAP vía rdap.org; WHOIS clásico (TCP 43) vía la cadena de reenvíos de whois.iana.org
- Tiempo límite de consulta
- 15 segundos en total (WHOIS_TIMEOUT)
- Conexión saliente
- reenvíos solo host[:43]; validados por SSRF y fijados antes de conectar; límite de 2 reenvíos; tope de 64 KiB; el dominio nunca es contactado
Disponibilidad de dominio — POST /api/v1/domain-availability
Responde si un nombre está libre para registrar, por TLD, con una máquina de estados honesta: AVAILABLE solo cuando la consulta RDAP responde 404 y los metadatos TLD integrados confirman que el registro opera RDAP; un no-encontrado solo-WHOIS permanece UNKNOWN (probablemente disponible); RESTRICTED se basa en metadatos. Soporte IDN completo (Unicode de entrada, punycode canónico, displayDomain de salida) y fan-out multi-TLD opcional (1–10 por solicitud). Premium es siempre unknown en el MVP: los precios son un subsistema futuro aparte.
POST /api/v1/domain-availability
Content-Type: application/json
{ "domain": "пример.рф", "tlds": ["com", "io"] }
{
"url": "пример.рф",
"displayDomain": "пример.рф",
"host": "xn--e1afmkfd.xn--p1ai",
"status": "UP",
"results": [
{
"tld": "com",
"availability": "AVAILABLE",
"reason": "rdap_not_found",
"confidence": "high",
"source": "rdap",
"premium": "unknown",
"checkedAt": "2026-09-03T12:00:02Z"
},
{
"tld": "io",
"availability": "UNKNOWN",
"reason": "tld_rdap_unknown",
"confidence": "low",
"source": "tld-metadata",
"premium": "unknown",
"checkedAt": "2026-09-03T12:00:02Z"
}
],
"tldMetaVersion": "2026-09-02",
"durationMs": 1240
}- TLDs por solicitud
- 1–10 únicos (Unicode o punycode); como máximo 4 consultas simultáneas bajo un presupuesto de 15 segundos (DOMAIN_AVAILABILITY_TIMEOUT)
- Veredictos
- AVAILABLE | REGISTERED | RESTRICTED | INVALID | UNKNOWN | ERROR — UNKNOWN nunca se disfraza de AVAILABLE; PREMIUM es inalcanzable en el MVP
- Caché
- di:avail 60s, di:reg 6h, fail-open; los resultados negativos nunca se guardan en caché
Antigüedad de dominio — POST /api/v1/domain-age
Informa la antigüedad de un dominio: la fecha de creación exacta del registro y un desglose determinista en años/meses/días. Proyección ligera sobre los datos WHOIS — una sola consulta sirve a disponibilidad, antigüedad y registrador dentro de la ventana de caché.
POST /api/v1/domain-age
Content-Type: application/json
{ "domain": "example.com" }
{
"url": "example.com",
"host": "example.com",
"status": "UP",
"age": {
"known": true,
"createdAt": "2019-01-15T05:00:00Z",
"ageInDays": 2809,
"years": 7,
"months": 7,
"days": 20
},
"source": "rdap",
"durationMs": 390
}- Cálculo de edad
- solo fechas UTC; años/meses completos con ancla de mes ajustada; días totales redondeados a la baja
- Antigüedad desconocida
- age.known=false + motivo acotado (no_data, inconsistent_data) — nunca una suposición
- Caché
- di:reg 6h compartida con disponibilidad/registrar (fail-open)
Comprobador de registrador — POST /api/v1/domain-registrar
Informa con qué registrador está registrado un dominio, normalizado contra una tabla de alias curada (GoDaddy, Namecheap, Cloudflare y más) — los nombres desconocidos se muestran exactamente como se publicaron. Incluye la organización del registro y la procedencia RDAP/WHOIS; comparte la caché de registro con antigüedad/disponibilidad.
POST /api/v1/domain-registrar
Content-Type: application/json
{ "domain": "example.com" }
{
"url": "example.com",
"host": "example.com",
"status": "UP",
"registrar": "GoDaddy.com, LLC",
"registrarCanonical": "GoDaddy",
"registrarIanaId": 625,
"registry": "Verisign, Inc.",
"source": "rdap",
"checkedAt": "2026-09-04T12:00:00Z",
"durationMs": 410
}- Registrador
- nombre visible + badge de alias canónico cuando coincide; el IANA ID muestra «—» (los datos del registro aún no lo incluyen)
- No publicado
- los registros con proxy de privacidad o censurados responden UP con motivo no_data — nunca una cáscara vacía
Generador de dominios — POST /api/v1/domain-generate
Genera dominios candidatos a partir de 1–3 palabras clave: formas directas, prefijos, sufijos, compuestos y formas cortas — deduplicados, ordenados y limitados. Puro e instantáneo: sin llamadas al registro durante la generación; las comprobaciones por candidato son acciones separadas y explícitas.
POST /api/v1/domain-generate
Content-Type: application/json
{ "keywords": ["coffee", "map"], "tlds": ["com", "io"], "limit": 24 }
{
"status": "UP",
"candidates": [
{ "domain": "coffee.com", "strategy": "direct" },
{ "domain": "coffee.io", "strategy": "direct" },
{ "domain": "getcoffee.com", "strategy": "prefix" },
{ "domain": "coffeemap.com", "strategy": "compound" }
],
"total": 24
}- Candidatos
- tope estricto 50, 24 mostrados por defecto; ≤10 TLDs filtrados por la instantánea de metadatos TLD
- Sin tráfico al registro
- la generación hace cero llamadas al registro — las comprobaciones por candidato usan el endpoint de disponibilidad con sus propios límites
Resumen de dominio — POST /api/v1/domain-overview
Un perfil compuesto: disponibilidad en el propio TLD, fecha de creación con el desglose determinista de antigüedad, registrador (normalizado, con número IANA), proyección de caducidad y los hallazgos de configuración del registro. Las secciones son independientes — una consulta fallida nunca vacía las demás.
POST /api/v1/domain-overview
Content-Type: application/json
{ "domain": "example.com" }
{
"url": "example.com",
"host": "example.com",
"status": "UP",
"availability": {
"tld": "com", "availability": "REGISTERED", "reason": "registered",
"confidence": "high", "source": "rdap", "premium": "unknown",
"checkedAt": "2026-09-05T12:00:00Z"
},
"registration": {
"known": true, "createdAt": "2019-01-15T05:00:00Z",
"ageYears": 7, "ageMonths": 7, "ageDays": 21,
"registrar": "GoDaddy.com, LLC", "registrarCanonical": "GoDaddy",
"registrarIanaId": 625, "registry": "Verisign, Inc.", "source": "rdap",
"checkedAt": "2026-09-05T12:00:01Z"
},
"expiry": {
"host": "example.com", "source": "rdap", "verdict": "ok",
"daysRemaining": 222, "expiration": "2027-04-15T04:00:00Z",
"checkedAt": "2026-09-05T12:00:02Z"
},
"holds": [],
"dnssec": [{ "id": "dnssec-absent", "state": "info", "severity": "info", "detail": "unsigned" }],
"tldMetaVersion": "2026-09-02",
"durationMs": 1620
}- Secciones
- disponibilidad (con puerta de metadatos) + registro (di:reg, 6h compartido) + caducidad (rdap.org) + hallazgos holds/dnssec
- Los hallazgos son hechos
- holds/DNSSEC son señales de configuración del registro — nunca veredictos de seguridad; DNS/SSL permanecen en sus herramientas independientes
Comprobador de seguridad — POST /api/v1/security-check
Revisión pasiva y consultiva de la configuración de una respuesta obtenida: cabeceras de seguridad (profundidad HSTS, aspectos CSP, COOP/COEP/CORP, X-XSS-Protection), flags de cookies, datos TLS, degradaciones de cadena y divulgación. Sin sondeos, sin afirmaciones de vulnerabilidad, sin notas; los valores de las cookies nunca salen del backend. El estado HTTP no condiciona la revisión (un 404/500 alcanzable se revisa).
POST /api/v1/security-check
Content-Type: application/json
{ "url": "https://example.com" }
{
"url": "https://example.com",
"finalUrl": "https://example.com/",
"status": "UP",
"durationMs": 610,
"summary": { "total": 14, "passed": 9, "warn": 3, "failed": 2 },
"findings": [
{ "id": "hsts-present", "state": "fail", "severity": "high" },
{ "id": "tls-cert", "state": "pass", "severity": "info", "detail": "valid" }
],
"cookies": [
{ "name": "session", "secure": false, "httpOnly": true, "sameSite": "Lax" }
],
"tls": { "enabled": true, "version": "TLS 1.3", "certStatus": "valid", "daysRemaining": 89 }
}- Alcance
- ~23 IDs de comprobación entre cabeceras/cookies/TLS/cadena/divulgación; solo solicitud pasiva
- Cookies
- Solo nombres y flags (Secure/HttpOnly/SameSite): los valores nunca salen del backend
- Tiempo límite
- 10 segundos en total (SECURITY_CHECK_TIMEOUT)
URL Analyzer — POST /api/v1/url-analyze
Desglose sintáctico puro de una URL: identidad canónica redactada (esquema, host, formas IDN, dominio registrable ingenuo, puerto, ruta, query, fragmento), parámetros ordenados con duplicados y clasificación de rastreo versionada, conteos de rastreo, señales informativas de seguridad y notas de normalización. Sin peticiones, sin DNS, sin almacenamiento: el análisis es local y tarda milisegundos.
POST /api/v1/url-analyze
Content-Type: application/json
{ "url": "https://xn--e1afmkfd.xn--p1ai/search?q=thing&utm_source=news&id=7&id=8" }
{
"status": "UP",
"identity": {
"hasCredentials": false,
"scheme": "https",
"host": "xn--e1afmkfd.xn--p1ai",
"displayHost": "пример.рф",
"registrableDomain": "пример.рф",
"isIdn": true,
"path": "/search",
"query": "q=thing&utm_source=news&id=7&id=8",
"normalized": "https://xn--e1afmkfd.xn--p1ai/search?q=thing&utm_source=news&id=7&id=8"
},
"parameters": [
{ "name": "q", "value": "thing", "classification": { "category": "unknown", "confidence": "none" } },
{ "name": "utm_source", "value": "news", "classification": { "category": "utm", "label": "utm_source", "confidence": "exact" } },
{ "name": "id", "value": "7", "occurrence": 1 },
{ "name": "id", "value": "8", "occurrence": 2 }
],
"tracking": { "utm": 1, "analytics": 0, "adsClickId": 0, "sessionSensitive": 0, "unknown": 3 },
"signals": ["idn_host"],
"normalization": { "schemeAssumed": false, "punycodeApplied": true, "defaultPortOmitted": false, "hostLowercased": false },
"registryVersion": "2026-09-1"
}- Alcance
- Componentes + parámetros ordenados + clasificación del registro + señales informativas; cero red
- Redacción
- Las contraseñas nunca se devuelven; los valores de parámetros sensibles se enmascaran (***) en todas partes, los nombres quedan visibles
URL Expander — POST /api/v1/url-expand
Expansión instantánea y sin persistencia de una cadena de redirecciones sobre el motor compartido: cada salto pasa la misma validación SSRF central, los bucles y el límite de 10 redirecciones se distinguen por reason, y la URL final recibe un análisis de rastreo completo. Las URLs devueltas se redactan — las contraseñas nunca salen del backend. Sin caché: las cadenas son sensibles al tiempo, la frescura es la respuesta correcta.
POST /api/v1/url-expand
Content-Type: application/json
{ "url": "https://bit.ly/3xyz" }
{
"url": "https://bit.ly/3xyz",
"status": "UP",
"finalUrl": "https://example.com/landing?utm_source=n",
"hopCount": 2,
"chain": [
{ "url": "https://bit.ly/3xyz", "status": 301, "location": "https://bit.ly/abc" },
{ "url": "https://bit.ly/abc", "status": 302, "location": "https://example.com/landing?utm_source=n" }
],
"analysis": { "status": "UP", "tracking": { "utm": 1, "analytics": 0, "adsClickId": 0, "sessionSensitive": 0, "unknown": 0 } },
"source": "engine",
"durationMs": 820
}- Alcance
- Cadena de saltos redactada + estado clasificado (bucle vs límite) + análisis de la URL final; sin DNS/TLS/tiempos por salto (terreno del Redirect Checker)
- Presupuesto de expansión
- 10 segundos en total (URL_EXPAND_TIMEOUT); límite de 10 saltos
UTM Builder — POST /api/v1/utm-build
Construcción determinista y sin estado de URLs de campaña: los parámetros no relacionados se conservan en orden, las UTM canónicas se sobrescriben o mantienen por campo, los duplicados se colapsan al último, el fragmento se conserva y los destinos con credenciales se rechazan in-band. La misma entrada produce siempre una salida idéntica byte a byte.
POST /api/v1/utm-build
Content-Type: application/json
{ "url": "https://example.com/landing?foo=1&utm_source=old#pricing", "utmSource": "newsletter", "utmMedium": "email", "utmCampaign": "spring_sale", "utmContent": "header_cta" }
{
"status": "UP",
"generatedUrl": "https://example.com/landing?foo=1&utm_source=newsletter&utm_medium=email&utm_campaign=spring_sale&utm_content=header_cta#pricing",
"warnings": ["existing_utm_overwritten:utm_source", "empty_field_skipped:utm_term"],
"parameters": { "utm_source": "newsletter", "utm_medium": "email", "utm_campaign": "spring_sale", "utm_content": "header_cta" }
}- Alcance
- Añadido de UTM en orden fijo + política preserve/overwrite/collapse + avisos; puro, sin red
- Determinismo
- Salida idéntica byte a byte para la misma entrada; campos ≤256 caracteres; campos desconocidos rechazados
Website Health — POST /api/v1/website-health
El diagnóstico compuesto: una petición normaliza el objetivo y ejecuta lanes concurrentes acotados sobre los motores existentes — una sola comprobación HTTP alimenta cinco secciones (disponibilidad, estado, tiempo de respuesta, cabeceras y configuración de seguridad); DNS, la sonda de certificados TLS y el perfil del dominio se ejecutan como lanes propias. El veredicto es un estado agregado con detalles por comprobación y enlaces profundos a las herramientas independientes. Los hallazgos describen solo las comprobaciones realizadas — nunca una garantía de seguridad.
POST /api/v1/website-health
Content-Type: application/json
{ "url": "https://example.com" }
{
"target": {
"canonical": "https://example.com/",
"displayHost": "example.com",
"scheme": "https",
"hasCredentials": false,
"isDomain": true,
"isHttp": true,
"valid": true
},
"overall": "WARNING",
"summaryKey": "health.summary.warning",
"checks": [
{ "type": "http_availability", "state": "pass",
"summaryKey": "health.check.httpAvailability.pass",
"value": "200", "durationMs": 612, "applicable": true,
"detailUrl": "/uptime-checker" },
{ "type": "http_status", "state": "pass",
"summaryKey": "health.check.httpStatus.pass",
"value": "200 OK", "applicable": true,
"detailUrl": "/http-status-checker" },
{ "type": "response_time", "state": "pass",
"summaryKey": "health.check.responseTime.pass",
"value": "612 ms", "applicable": true,
"detailUrl": "/response-time-checker" },
{ "type": "headers", "state": "warn",
"summaryKey": "health.check.headers.warn",
"value": "2 missing", "reason": "findings", "applicable": true,
"detailUrl": "/security-checker" },
{ "type": "security", "state": "warn",
"summaryKey": "health.check.security.warn",
"value": "14 of 21 passed", "reason": "findings", "applicable": true,
"detailUrl": "/security-checker" },
{ "type": "tls", "state": "pass",
"summaryKey": "health.check.tls.pass",
"value": "TLS 1.3 · valid, 89 days", "durationMs": 140,
"applicable": true, "detailUrl": "/ssl-checker" },
{ "type": "dns", "state": "pass",
"summaryKey": "health.check.dns.pass",
"value": "A, AAAA, MX, NS", "durationMs": 95,
"applicable": true, "detailUrl": "/dns-checker" },
{ "type": "domain", "state": "pass",
"summaryKey": "health.check.domain.pass",
"value": "expires in 214 days", "durationMs": 730,
"applicable": true, "detailUrl": "/domain-overview" }
],
"meta": { "checksTotal": 8, "checksPassed": 6, "checksWarn": 2,
"checksFailed": 0, "checksUnknown": 0,
"tldMetaVersion": "2026-09-03", "durationMs": 1480 }
}- Alcance
- Una ejecución HTTP alimenta cinco secciones; lanes de DNS/TLS/dominio; agregado HEALTHY/WARNING/ERROR/PARTIAL/UNKNOWN
- Límites
- Presupuesto global de 25 s + presupuestos por lane, ≤3 lanes simultáneas; objetivos inválidos in-band; sin caché — frescura primero
Uptime Monitoring — la API de gestión /api/v1/monitors
La excepción con estado respecto a las herramientas instantáneas: crea un monitor para una URL y el worker lo comprueba en un calendario fijo, con historial de ejecuciones, estadísticas de disponibilidad, episodios de incidentes y notificaciones webhook. La propiedad es una clave de gestión que se muestra exactamente una vez al crearla; cada lectura y escritura se autentica con ella, y una clave incorrecta es indistinguible de un monitor inexistente (404 uniforme).
POST /api/v1/monitors
Content-Type: application/json
{ "url": "https://example.com", "name": "Prod site", "interval": 300 }
201 Created
{
"monitor": { "id": "b1…", "name": "Prod site",
"normalizedTarget": "https://example.com/",
"state": "unknown", "interval": 300 },
"manageUrl": "/monitors/b1…?key=…",
"key": "…shown exactly once…"
}
GET /api/v1/monitors/{id}?key=… detail + stats window + incidents
GET /api/v1/monitors/{id}/history cursor-paginated executions
GET /api/v1/monitors/{id}/history/daily per-day buckets (30-day cap)
GET /api/v1/monitors/{id}/incidents incident episodes
POST /api/v1/monitors/{id}/check enqueue a priority manual check
PATCH /api/v1/monitors/{id} name / interval / webhook / pause
DELETE /api/v1/monitors/{id} cascade delete
Credentials in the target are rejected (credentials_not_allowed); the
target is SSRF-pre-validated at creation and re-validated on every run.- Alcance
- Crear / listar / detalle / pausa / eliminar / comprobación manual / historial / incidentes; intervalos fijos de 60–3600s; INVALID_URL y BLOCKED_TARGET en banda al crear
- Gestión de claves
- Clave de 256 bits mostrada una sola vez (se guarda el sha256); las credenciales en los objetivos se rechazan; SSRF prevalidado en la creación y revalidado en cada ejecución
Páginas de estado públicas — la API de gestión /api/v1/status-pages
Componga sus monitores en una página de estado compartible: elija componentes, ordénelos, ajuste los nombres visibles y publíquela bajo un slug estable. Las páginas viven bajo la misma clave de gestión que los monitores; los borradores son privados, publicar es idempotente y reversible, y cada mutación borra la entrada de la caché pública: los cambios de acceso aplican dentro de la propia petición.
POST /api/v1/status-pages
Content-Type: application/json
X-Monitor-Key: <your key>
{ "name": "Prod API" }
201 Created
{
"page": { "publicId": "p1…", "name": "Prod API",
"slug": "prod-api", "published": false },
"manageUrl": "/status-pages/p1…?key=…"
}
GET /api/v1/status-pages/{id}?key=… editor payload (members + monitors)
PATCH /api/v1/status-pages/{id} name/description/slug + membership
POST /api/v1/status-pages/{id}/publish → live at /status/{slug}
POST /api/v1/status-pages/{id}/unpublish → removed within the request
DELETE /api/v1/status-pages/{id} cascade delete
GET /api/v1/public/status-pages/{slug} the public DTO (no key; cached 60s)
Membership is same-key only (≤25 monitors, replace-list semantics);
publish requires ≥1 visible member; the public DTO is an explicit
projection — target URLs, keys and internal ids never cross.- Alcance
- Crear / listar / editar / lista de membresía de reemplazo (≤25, solo la propia clave) / publicar / retirar / eliminar; slugs de 3-63 caracteres con palabras reservadas; máx. 5 páginas por clave
- Superficie pública
- GET /api/v1/public/status-pages/{slug} + /status/{slug}; solo un DTO público explícito — las URLs de destino, claves, hosts de notificación e ids internos nunca cruzan; caché Redis 60s, fail-open
Acortador de URL — la API de gestión /api/v1/short-links
Convierte una URL larga en un enlace corto /go/{code} que puedes gestionar: destino validado, código aleatorio criptográfico, estadísticas de clics con ventanas de hoy/7 días/30 días. Los enlaces viven bajo la misma clave de gestión que tus monitores y páginas de estado: la creación se adjunta a una clave existente y nunca genera una nueva. Los destinos se validan al crear o editar (sintaxis, credenciales rechazadas, clasificación SSRF); la redirección pública nunca obtiene nada y responde 302 sin caché.
POST /api/v1/short-links
Content-Type: application/json
X-Monitor-Key: <your key>
{ "destinationUrl": "https://example.com/launch?utm_source=newsletter" }
201 Created
{
"link": { "id": "sl1…", "code": "a3f9k2mz",
"shortUrl": "/go/a3f9k2mz",
"destination": "https://example.com/launch?utm_source=newsletter" }
}
GET /api/v1/short-links?key=… your links (newest first)
GET /api/v1/short-links/{id}?key=… full link + stats
PATCH /api/v1/short-links/{id}?key=… destination/title/expiry (re-validated)
POST /api/v1/short-links/{id}/disable public 404, stats kept (enable restores)
DELETE /api/v1/short-links/{id}?key=… soft delete (idempotent)
GET /api/v1/short-links/{id}/stats?key=… total + today/7d/30d clicks
GET /go/a3f9k2mz → 302 Found + Location (Cache-Control: no-store);
unknown/disabled/expired/deleted all answer 404.
Destinations are validated at create/update (http/https, credentials
rejected, SSRF classification); the redirect itself never fetches.- Alcance
- Crear / listar / editar / deshabilitar / habilitar / borrado lógico / estadísticas; ≤100 enlaces activos por clave, 30 creaciones por clave y hora; el destino se revalida en cada cambio
- Redirección pública
- GET /go/{code} → 302 + no-store; desconocido/deshabilitado/caducado/eliminado responden el mismo 404 neutro; la redirección es una pura búsqueda — nunca acepta un parámetro de destino ni resuelve u obtiene el objetivo; sin IPs ni user-agents almacenados
Denunciar un enlace corto — la API anónima de abusos
Si un enlace /go/{code} parece phishing, malware, spam o estafa, denúncialo — no hace falta cuenta y no recibirás respuesta. Las denuncias llegan a la cola de revisión del operador; solo el operador puede retirar un enlace. El endpoint responde igual a toda denuncia bien formada: quien denuncia no averigua si el código existe.
POST /api/v1/short-link-reports
Content-Type: application/json
{ "code": "a3f9k2mz", "reason": "phishing",
"detail": "Imitates a login page" }
202 Accepted
{ "status": "received" }
Reasons: phishing | malware | spam | scam | other (detail ≤500 chars,
optional plain text). The SAME 202 comes back for unknown, disabled
and deleted codes — no existence oracle. Limits per IP: one report
per code per 10 min, 10/hour, 30/day (429 + Retry-After; Redis
failure = 503, fail-closed). Reports go to the operator queue; only
the operator can take a link down (the owner key cannot override).- Límites y privacidad
- 1 denuncia por código cada 10 min por IP, luego 10/hora y 30/día (429 + Retry-After; ante errores de Redis, 503 fail-closed); motivos phishing | malware | spam | scam | other; sin cuenta, sin correo, sin IP cruda — solo una huella rotativa diaria
- Respuesta uniforme
- toda denuncia bien formada recibe el mismo 202 received — para códigos vivos, desactivados, borrados o desconocidos (sin oráculo de existencia); un código malformado o un motivo desconocido responde 400 antes de cualquier búsqueda
Cuentas y claves API — /api/v1/auth/register y /api/v1/api-keys
Cuentas de navegador (correo + contraseña, registro abierto; el correo no se verifica en esta versión) con sesiones del lado del servidor en una cookie endurecida. Las claves API autentican llamadas de servidor a servidor sobre una lista explícita de familias de recursos. Los fallos de inicio de sesión son genéricos: correo desconocido y contraseña errónea son indistinguibles, incluso por tiempo de respuesta.
POST /api/v1/auth/register
Content-Type: application/json
{ "email": "you@example.com", "password": "…", "displayName": "…" }
201 Created + Set-Cookie: wg_session (HttpOnly; Secure; SameSite=Lax)
{ "account": { "id": "acc…", "email": "you@example.com", … } }
POST /api/v1/auth/login 200 + Set-Cookie (401 generic on failure)
POST /api/v1/auth/logout deletes the server-side session
GET /api/v1/account the session's own profile
PATCH /api/v1/account update display name (displayName)
GET /api/v1/account/summary account-owned resource counts
POST /api/v1/account/claim management key → re-own key-held resources
POST /api/v1/api-keys (SESSION) create; secret shown ONCE
{ "name": "CI", "scopes": ["monitors:read"] }
GET /api/v1/api-keys (SESSION) list — no secrets
POST /api/v1/api-keys/{id}/revoke (SESSION) immediate 401 on next use
Bearer access (Authorization: Bearer wg_live_…) is an EXPLICIT
allowlist only: account GET/PATCH, monitors CRUD + reads, status-pages
GET, short-links CRUD + stats; everything else refuses API keys.
Missing/unknown/revoked → generic 401; wrong scope → 403; per-key and
per-account rate limits → 429 + Retry-After.- Lista Bearer
- account GET/PATCH (account:read), monitores (lectura/escritura), páginas de estado GET (lectura), enlaces cortos (lectura/escritura); todo lo demás rechaza claves API. Los recursos de clave no vinculados son invisibles hasta adjuntarse vía claim-by-key.
- Manejo del secreto
- La clave wg_live_ completa se muestra exactamente una vez al crearla; solo se guarda un hash unidireccional. Claves ausentes/desconocidas/revocadas responden un 401 genérico único; ámbito insuficiente → 403; límites → 429 + Retry-After.
Identidad de correo de la cuenta (confirmación y recuperación)
Base de correo de la fase J: confirma tu dirección, recupera una contraseña perdida y demuestra conocimiento reciente de la contraseña para acciones sensibles.
POST /api/v1/auth/verification/request (SESSION) 202 {accepted:true}
POST /api/v1/auth/verification/confirm { "token": "…" } → 200 {verified:true}
POST /api/v1/auth/password-reset/request { "email": "you@example.com" } → 202 (always the same answer)
POST /api/v1/auth/password-reset/confirm { "token": "…", "password": "…" } → 200 {reset:true}
POST /api/v1/auth/reauthenticate (SESSION) { "password": "…" } → 200 {confirmed:true}
Reset revokes ALL sessions in the same transaction; API keys stay
active. Tokens ride mail links as URL fragments — never query strings —
and every mismatch (wrong account, replay, expiry, unknown) answers the
same generic 404.- Tokens
- de un solo uso, 24 h (verificación) / 30 min (restablecimiento), solo en el fragmento, 404 genérico ante cualquier discrepancia
- Restablecimiento
- cierra todas las sesiones de forma atómica; las claves API siguen válidas
- Reautenticación
- 5 minutos, del lado del servidor, ligada a la sesión
- Espacios de trabajo
- GET /api/v1/workspaces — personal + organizaciones, no-store, solo sesión
- Organizaciones
- crear/listar/leer/renombrar/eliminar + transferencia de propiedad; sesiones verificadas, versiones optimistas, reauth para acciones destructivas
- Invitaciones
- invitaciones por correo con enlaces de un solo uso de 7 días, cuatro roles fijos, límites con reserva de plazas y ventanas de frecuencia obligatorias
- Recursos de organización
- monitores/páginas/enlaces compartidos bajo /organizations/{orgId}; RBAC de sesión, mutaciones versionadas, claves legacy excluidas en SQL
- Traslado de recursos
- mover monitores/páginas/enlaces personales a una organización de forma atómica (cierre ≤100, versiones verificadas, idempotente)
- Claves de organización
- claves de servicio (wg_live_…), se muestran una vez, con ámbitos, sobreviven a la salida del creador, revocación inmediata
- Auditoría de organización
- historial de solo adición de cambios confirmados (365 días), lectura Owner/Admin, divulgación mínima de actores