Todo lo que hace el KIT, en un solo lugar
ForHosting KIT es un catálogo de 7447 tareas listas — convertir un documento, leer una factura, transcribir audio, validar un IBAN, generar un QR — repartidas en 14 categorías. Las ejecuta aquí en la web o las llama desde su código con un POST autenticado. Esta página es la referencia completa de la API: endpoints, modelo asíncrono, contrato de webhooks, errores y precios.
Qué es el KIT
Un catálogo de tareas, no un modelo
Cada capacidad es una tarea: manda una entrada y recibe un resultado. No hay tokens, ni ventana de contexto, ni prompt engineering. Una tarea tiene precio publicado, unidad documentada y forma fija.
Cuatro formas de ejecutarla. Web — cada capacidad tiene su página y corre en el navegador; las gratuitas no salen de su dispositivo. API — un POST autenticado, documentado abajo. Email y Telegram — manda la tarea a una dirección del KIT. La API es un canal, no el producto.
Empezar
De cero a un resultado en tres llamadas
Crea la cuenta, recoge la clave, ejecuta. Sin llamada comercial ni lista de espera.
Consiga una API key
POST /signup con su email devuelve una clave que empieza por kit_live_. Se enseña una sola vez. Solo guardamos su hash SHA-256, así que si la pierde no podemos recuperarla — emitimos otra.
Elija una capacidad
GET /catalog lista las 7447 con su precio y unidad en vivo. O mire el catálogo al final de esta página.
Ejecútela
POST al endpoint de la capacidad. Recibe un task_id al instante y el resultado llega a su webhook.
Autenticación
Token Bearer, se enseña una vez
Toda petición a la API lleva Authorization: Bearer kit_live_…. Las claves son 48 caracteres hexadecimales tras el prefijo.
Guardamos solo el hash SHA-256 de su clave. Es a propósito: un volcado de la base de datos no le entrega sus credenciales a nadie — pero también significa que de verdad no podemos reenviársela. ¿La perdió? Revocamos y emitimos otra.
Una clave mala siempre devuelve 401 y nunca dice por qué. Revocada, mal escrita e inexistente son indistinguibles a propósito: decirte cuál era se lo dice también a quien está probando.
El modelo asíncrono
Toda tarea es asíncrona. Sin excepciones.
Hace POST de la tarea
Devuelve 202 con un task_id y estado queued, más lo que va a costar. El dinero se retiene, no se cobra.
Se ejecuta en el edge
Sub-segundo en cómputo; segundos en IA y medios.
El resultado te busca
Se hace POST a tu webhook_url si diste uno. Si no, GET /tasks/{id}/result. Los resultados se guardan según su tamaño: desde 168 h para los pequeños (hasta 1 MB) hasta 6 h para los más grandes.
Fallar no cuesta
Una tarea se intenta hasta 3 veces en total, con backoff entre intentos. Si aun así falla, se libera la retención y no se te cobra. Nunca.
Referencia de la API
Todos los endpoints que responde el KIT
Las rutas no llevan prefijo de versión. /v1/* sigue resolviendo por compatibilidad, pero no es la forma canónica y el código nuevo no debería usarla.
URL base: https://api.kit.forhosting.com
| Método | Ruta | Auth | Qué hace |
|---|---|---|---|
ANY |
/ |
Pública | Índice del servicio: versión, número de capacidades y la lista de endpoints que el API anuncia de sí mismo. Sin clave, y responde a cualquier método. |
POST |
/{alias} |
API key | Atajo por capacidad de POST /tasks con el type fijo — p.ej. POST /ocr/invoice. Es la forma que enseña cada página de capacidad. |
GET |
/account |
API key | Saldo de la cuenta: available_usd es el saldo real del monedero, más held_usd. Si el monedero lo gestiona el área de cliente, balance.source es "portal" y balance_endpoint apunta al valor en vivo. |
POST |
/agent/ask |
API key Pronto | No implementado — devuelve 501 con clave y 401 sin ella. El asistente conversacional se está construyendo aparte; para encontrar una capacidad use POST /catalog/search. |
GET |
/catalog |
Pública | Todas las capacidades con precio y unidad en vivo. ?lang=en|es, ?q= para filtrar, ?limit=, ?schema=1 para el esquema de entrada y ?channel= para recibir el precio ya ajustado a ese canal — pida el canal por el que va a facturar, o enseñará una cifra y cobrará otra. |
POST |
/catalog/search |
Pública | {query} → las capacidades que encajan, cada una con su precio ya redactado. Sin clave. Puntúa por cobertura de palabra entera con umbral, así que una consulta que no entiende devuelve nada en vez de adivinar — es a propósito. |
POST |
/estimate |
API key | {type, input} → unidades, precio y desglose. Cotiza sin ejecutar. Cuando la cantidad real no se puede saber de antemano (las páginas de un PDF que llega por URL), la respuesta avisa con estimated: true. |
GET |
/mobile/bootstrap |
Pública | Lo que la app móvil necesita para arrancar: categorías, etiquetas y los mismos precios ajustados al canal. Sin clave. Tampoco forma parte del contrato público, por el mismo motivo. |
GET |
/mobile/catalog |
Pública | Proyección del catálogo para la app móvil, con los precios ya ajustados al canal de la app. Sin clave. No forma parte del contrato público: su forma sigue a la app y puede cambiar sin aviso — programe contra GET /catalog. |
GET |
/plans |
Pública | Importes de recarga: currency y topup (sku, default_amount, min_amount). Nada más — no hay planes a los que suscribirse. |
POST |
/signup |
Pública | {email} → 201 con tu api_key, mostrada una vez. 409 si el email ya existe; 429 pasadas 10/h por IP. |
GET |
/tasks |
API key | Tus tareas. ?status=, ?limit= (25 por defecto, 100 máximo). |
POST |
/tasks |
API key | {type, input, webhook_url?, max_cost_usd?} → 202. Se factura por la unidad real de la tarea — páginas, minutos, imágenes — medida al ejecutarla, no por la estimación previa. max_cost_usd es un tope duro: si el coste real lo supera, la tarea falla y no se cobra nada. Manda Idempotency-Key para que reintentar sea seguro: una repetición devuelve la tarea original con idempotent: true. |
DELETE |
/tasks/{id} |
API key | Cancela una tarea en cola y libera su retención. |
GET |
/tasks/{id} |
API key | Estado de la tarea. 10 consultas cada 10 segundos por tarea; por encima, 429 con Retry-After: 1. Mejor use el webhook. |
GET |
/tasks/{id}/events |
API key Pronto | No implementado — devuelve 501. El SSE llegará; usa el webhook. |
GET |
/tasks/{id}/result |
API key | Resultado JSON, o el fichero como adjunto. 409 si no está listo, 410 si caducó, 422 si falló. La retención depende del tamaño del resultado: 168 h para los pequeños, hasta 6 h para los muy grandes. |
POST |
/tasks/{id}/retry |
API key | Reencola una tarea fallida. |
POST |
/uploads |
API key | Manda un archivo local: cuerpo binario crudo, con el Content-Type del archivo. → 201 con ref: "kit://upl_…", que luego pones donde iría una URL: {"input": {"pdf": "kit://upl_…"}}. Una subida puede alimentar varias tareas. Los refs viven 24 h. Máximo 100 MiB (104.9 MB) por subida; cada capacidad aplica además su propio límite. |
Webhooks
Entrega firmada, y cómo verificarla
Pon webhook_url al crear la tarea y hacemos POST del resultado ahí cuando está listo. Es la vía recomendada: cuesta menos que sondear y llega antes.
Verifica la firma antes de fiarte del cuerpo. Cada entrega lleva KIT-Signature: v1=<hex> y KIT-Timestamp: <segundos unix>. La firma es HMAC-SHA256 sobre la cadena <timestamp>.<cuerpo crudo> — el timestamp y el punto son parte de lo firmado, no adorno. Firma los bytes crudos que recibiste, no un objeto re-serializado.
La entrega se intenta hasta 5 veces con backoff exponencial. Un 4xx desde su endpoint corta los reintentos de inmediato — lo leemos como «su handler está mal», no como «pruebe luego». Solo se reintentan los 5xx y los errores de red. Después, la entrega va a la cola de fallidos.
Lo que enviamos
{
"event": "task.completed",
"created_at": "2026-07-16T10:31:04.120Z",
"data": {
"task_id": "tsk_a1b2c3d4e5f6",
"type": "ocr.invoice",
"status": "done",
"units": 1,
"price_usd": 0.021,
"result_url": "https://api.kit.forhosting.com/tasks/tsk_a1b2c3d4e5f6/result"
}
}{
"event": "task.failed",
"created_at": "2026-07-16T10:31:04.120Z",
"data": {
"task_id": "tsk_a1b2c3d4e5f6",
"type": "ocr.invoice",
"status": "failed",
"error": {
"code": "engine_error",
"message": "Upstream timed out after 3 attempts."
},
"charged": false
}
}Verificar la firma
const crypto = require("crypto");
// req.body tiene que ser el cuerpo CRUDO, no un objeto re-serializado.
function verify(rawBody, headers, secret) {
const sig = (headers["kit-signature"] || "").replace(/^v1=/, "");
const ts = headers["kit-timestamp"];
const mine = crypto.createHmac("sha256", secret)
.update(ts + "." + rawBody) // el timestamp va firmado
.digest("hex");
return crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(mine));
}import hmac, hashlib
def verify(raw_body: bytes, headers, secret: str) -> bool:
sig = headers["KIT-Signature"].removeprefix("v1=")
ts = headers["KIT-Timestamp"]
mine = hmac.new(secret.encode(),
f"{ts}.".encode() + raw_body, # el timestamp va firmado
hashlib.sha256).hexdigest()
return hmac.compare_digest(sig, mine)Errores
HTTP estándar, slug legible por máquina
Todo error lleva un error estable en el cuerpo. Compara contra el slug, no contra el mensaje: los mensajes están localizados y pueden cambiar.
| HTTP | Error | Significado |
|---|---|---|
400 | flow_depth | Una capacidad compuesta se anidó más de lo permitido. Aplane los pasos. |
400 | invalid_amount | El importe falta, no es un número, o está fuera del rango permitido. |
400 | invalid_email | El email falta o no es válido. |
400 | invalid_input | Al input le falta un campo obligatorio o no tiene la forma que espera esta tarea. |
400 | invalid_json | El cuerpo de la petición no es JSON válido. |
400 | missing_type | Falta el campo 'type' en la petición. Consulte GET /catalog. |
400 | unknown_op | Operación desconocida para esta capacidad. Compruebe el nombre contra el catálogo. |
400 | unsafe_url | La URL apunta a un sitio del que no vamos a descargar: una dirección interna o no pública. |
401 | auth_required | Este paso necesita una cuenta identificada y la petición no la trae. |
401 | no_key | La petición no trae API key, o la cuenta no tiene ninguna activa. |
401 | unauthorized | API key ausente o inválida. |
402 | account_suspended | Su cuenta está suspendida, normalmente por el tope de gasto. Escríbanos para reactivarla. |
402 | insufficient_balance | El saldo no cubre el precio de la tarea. |
402 | max_cost_exceeded | La tarea costó más que el max_cost_usd que fijaste. No se ha cobrado nada. |
403 | forbidden | La credencial es válida pero no tiene permiso para esto. |
404 | input_not_found | Esa referencia kit:// no existe. Vuelve a subir el archivo con POST /uploads. |
404 | not_found | No hay ninguna tarea ni recurso en esa ruta. |
404 | unknown_type | El tipo de tarea no existe. |
409 | alias_taken | Ese alias de correo entrante es de otra persona. Elija otro. |
409 | already_accepted | Esa aceptación ya se usó. Cada una vale exactamente una vez. |
409 | email_taken | Ya existe una cuenta con ese email. |
409 | need_lease | Otro trabajador ya está ejecutando esta tarea. Espere a que termine. |
409 | not_cancellable | La tarea ya no está en cola, así que no se puede cancelar. Solo se cancelan las encoladas. |
409 | not_ready | La tarea aún no ha terminado. Sondee su estado o espere el webhook. |
409 | not_retryable | Solo se reintentan las tareas fallidas. Esta está en otro estado. |
410 | expired | La ventana de retención del resultado ya pasó y no se conserva. |
410 | input_expired | Esa referencia kit:// caducó. Las subidas viven 24 h; vuelve a subir el archivo. |
410 | quote_expired | La cotización pasó su ventana de validez. Pida una nueva. |
413 | input_too_large | El archivo supera el límite de tamaño. |
413 | resolution_too_high | La imagen o el vídeo superan la resolución que acepta esta capacidad. |
422 | conversion_failed | No se pudo convertir el fichero. Suele ser un formato corrupto o inesperado. |
422 | engine_error | El motor falló en todos los intentos. Se liberó la retención: no se le cobra. |
422 | needs_rework | El resultado no pasó su propio control de calidad, así que no se entrega. No se cobra. |
429 | rate_limited | Demasiadas peticiones. Use el webhook en vez de sondear. |
500 | lease_error | No se pudo tomar la tarea para ejecutarla. Vuelve a la cola. |
500 | ledger_error | No se pudo reservar ni liquidar el saldo. Inténtelo de nuevo. |
500 | no_hold | La tarea no tiene reserva que liquidar. No debería ocurrir; si ocurre, avísenos. |
500 | no_result | La tarea terminó sin producir resultado. |
501 | coming_soon | Capacidad en despliegue (Fase 1b). Su landing existe; aún no acepta ejecuciones. |
501 | mail_not_configured | Esta capacidad envía correo y la cuenta aún no tiene remitente configurado. |
501 | not_implemented | Este endpoint o capacidad aún no está disponible. |
501 | tickets_not_configured | La integración de tiques no está configurada para esta cuenta. |
501 | unsupported | Esta operación aún no se admite (por ejemplo, los flows no se pueden cotizar). |
502 | model_output_invalid | El modelo devolvió algo que no encaja con la salida declarada. No se cobra. |
502 | tickets_unreachable | El sistema de tiques no respondió. No se cobró nada; inténtelo otra vez. |
503 | all_busy | Todos los trabajadores de esta capacidad están ocupados. Reintente en un momento. |
Precios
Publicados, por tarea, sin créditos
Pague solo por lo que usa. Añada saldo a su cuenta (desde $10.00) — no caduca — y cada tarea se descuenta a su precio por tarea publicado. Dólares reales, no puntos.
Cada tarea cuesta una tarifa base más una tarifa por unidad, las dos publicadas en la página de la capacidad y en el catálogo de abajo — desde $0.002 por llamada. POST /estimate cotiza una tarea sin ejecutarla, y max_cost_usd en la tarea la rechaza si fuera a costar más de lo que dijiste.
Las tareas fallidas no se cobran nunca. Las capacidades gratuitas corren en su navegador y no cuestan nada en absoluto.
Límites
Lo que el servicio impone
Tres límites, y cada uno cuenta por su lado. Por cuenta: 600 peticiones por minuto en total, y 60 subidas por minuto. Por tarea: 10 consultas de estado cada 10 segundos — al pasarse cualquiera de ellos recibe 429 con Retry-After: 1. Use el webhook en lugar de sondear: cuesta menos y llega antes. Los resultados se guardan según su tamaño: 168 h hasta 1 MB, y hasta 6 h para los muy grandes. GET /tasks devuelve 25 por defecto y 100 como máximo. El alta está limitada a 10 cuentas por hora y por IP.
Catálogo de capacidades
Las 7447, navegables por categoría
Cada capacidad tiene su propia página, donde la ejecuta, ve un ejemplo real y el precio publicado — el mismo que factura esta API. Abra el catálogo completo, o vaya directo a una categoría.
Sin garantía
Lo que no prometemos
El KIT se ofrece «tal cual»: sin garantía, sin compromiso de disponibilidad, sin SLA (la infraestructura dedicada con SLA es un extra de pago) y sin soporte garantizado. Ver la cláusula KIT completa en los Términos.