Referencia de la API
Autentícate y consulta la API de Hiring para ofertas de empleo, candidatos y candidaturas.
Por qué es importante
La API de Hiring da a los sistemas externos acceso de lectura a tus datos de contratación: los portales de empleo obtienen las ofertas publicadas, los sistemas HRIS sincronizan los registros de candidatos, los dashboards rastrean las métricas del pipeline. Los scopes de los tokens garantizan que cada integración solo vea lo que necesita.
URL base
Todos los endpoints de la API son accesibles a través de:
https://startupkit.app/api/v1
Autenticación
Cada solicitud requiere un token de API en el encabezado Authorization:
Authorization: Bearer YOUR_TOKEN
Formato alternativo (ambos funcionan):
Authorization: token YOUR_TOKEN
Crea tokens en Settings > API. Cada token debe tener scopes explícitos para acceder a los endpoints de contratación.
| Scope | Acceso |
|---|---|
job_postings:read |
Ofertas de empleo y etapas |
candidates:read |
Datos personales del candidato (nombre, email, teléfono), metafields de la candidatura, descarga de CV |
applications:read |
Candidaturas, historial de etapas, descarga de CV |
Los tokens sin ningún scope no pueden acceder a los endpoints de contratación.
Formato y ciclo de vida del token
-
Formato: Cadena base58 de 24 caracteres (p. ej.,
k3Jf9pQ2mZ7xR4nL8vT6bWyH) - Se muestra una sola vez: El token en bruto se muestra una única vez, en la pantalla de confirmación justo después de crearlo. Cópialo en ese momento; después no se puede volver a recuperar.
- Solo se almacena el hash: Kit solo guarda un hash SHA-256 del token, nunca el texto plano. Si pierdes el token, revócalo y crea uno nuevo; el valor original no se puede recuperar.
-
Rastreo de último uso: Cada solicitud exitosa actualiza
last_used_at - Sin fecha de expiración: Los tokens no caducan por tiempo. No hay ninguna opción de expiración en el formulario de creación, y Settings > API no muestra ninguna: un token sigue siendo válido hasta que se revoca.
- Alertas de inactividad: Los tokens sin usar durante más de 30 días activan notificaciones de administrador
- Revocación automática: Los tokens se revocan automáticamente cuando un usuario es eliminado de la cuenta
Buena práctica: Crea tokens separados por integración con los scopes mínimos necesarios.
Alcance por cuenta
Cada token de API está vinculado a una cuenta específica. La cuenta se determina automáticamente a partir del token; no se necesita ningún parámetro account_id.
Cuando un miembro del equipo es eliminado de una cuenta, sus tokens para esa cuenta se revocan automáticamente.
Respuestas de error
| Estado | Significado | Causas comunes |
|---|---|---|
400 |
Solicitud incorrecta | Contexto de cuenta ausente (caso límite en autenticación por sesión) |
401 |
No autorizado | Token ausente, inválido o revocado |
403 |
Prohibido | El token no tiene el scope requerido para este endpoint |
404 |
No encontrado | El recurso no existe o no pertenece a tu cuenta |
422 |
Entidad no procesable | Errores de validación (principalmente para endpoints fuera de contratación) |
Paginación
Los endpoints de lista devuelven resultados paginados:
{
"data": [...],
"pagination": {
"current_page": 1,
"total_pages": 3,
"total_count": 42,
"per_page": 20
}
}
Pasa ?page=2 para obtener las páginas siguientes.
Ofertas de empleo
Scope requerido: job_postings:read
Listar ofertas de empleo
GET /api/v1/job_postings
Ejemplo:
curl -H "Authorization: Bearer YOUR_TOKEN" \
https://startupkit.app/api/v1/job_postings?status=published
Parámetros:
| Parámetro | Tipo | Descripción |
|---|---|---|
status |
string | Filtrar por draft, published, paused o closed
|
page |
integer | Número de página |
Respuesta:
{
"data": [
{
"id": "job_abc123",
"title": "Senior Rails Developer",
"status": "published",
"department": "Engineering",
"location": "New York, NY",
"employment_type": "full_time",
"remote": true,
"salary_min": "120000.0",
"salary_max": "180000.0",
"salary_currency": "USD",
"salary_period": "year",
"published_at": "2026-01-15T10:00:00Z",
"closed_at": null,
"created_at": "2026-01-10T08:30:00Z",
"updated_at": "2026-02-01T14:20:00Z",
"stages": [
{
"id": "stg_def456",
"name": "Screening",
"stage_type": "default",
"position": 0
}
],
"counts": {
"total_applications": 24,
"active_applications": 18,
"rejected": 6
}
}
],
"pagination": { ... }
}
Obtener una oferta de empleo
GET /api/v1/job_postings/:id
Devuelve la misma estructura que un elemento de la respuesta de lista.
Listar etapas
GET /api/v1/job_postings/:job_posting_id/stages
Respuesta:
{
"data": [
{
"id": "stg_def456",
"name": "Screening",
"stage_type": "default",
"position": 0
}
]
}
Las etapas están ordenadas por posición. Sin paginación.
Candidatos
Scope requerido: candidates:read
Listar candidatos
GET /api/v1/candidates
Ejemplo:
curl -H "Authorization: Bearer YOUR_TOKEN" \
https://startupkit.app/api/v1/[email protected]
Parámetros:
| Parámetro | Tipo | Descripción |
|---|---|---|
search |
string | Buscar por nombre o email |
page |
integer | Número de página |
Respuesta:
{
"data": [
{
"id": "cand_ghi789",
"first_name": "John",
"last_name": "Doe",
"email": "[email protected]",
"phone": "+1-555-0100",
"status": "active",
"github_username": "johndoe",
"created_at": "2026-01-20T09:15:00Z",
"applications": [
{
"id": "app_jkl012",
"job_posting_id": "job_abc123",
"job_posting_title": "Senior Rails Developer",
"current_stage_name": "Interview",
"status": "active",
"submitted_at": "2026-01-20T09:15:00Z"
}
]
}
],
"pagination": { ... }
}
Obtener un candidato
GET /api/v1/candidates/:id
Devuelve la misma estructura que un elemento de la respuesta de lista.
Candidaturas
Scope requerido: applications:read
Listar candidaturas
GET /api/v1/applications
Ejemplo:
curl -H "Authorization: Bearer YOUR_TOKEN" \
https://startupkit.app/api/v1/applications?job_posting_id=job_abc123&status=active
Parámetros:
| Parámetro | Tipo | Descripción |
|---|---|---|
job_posting_id |
string | Filtrar por ID con prefijo de la oferta de empleo |
status |
string | Filtrar por active o rejected
|
page |
integer | Número de página |
Respuesta:
{
"data": [
{
"id": "app_jkl012",
"job_posting_id": "job_abc123",
"candidate_id": "cand_ghi789",
"current_stage_name": "Interview",
"status": "active",
"submitted_at": "2026-01-20T09:15:00Z",
"created_at": "2026-01-20T09:15:00Z",
"updated_at": "2026-02-10T11:30:00Z",
"candidate": {
"id": "cand_ghi789",
"first_name": "John",
"last_name": "Doe",
"email": "[email protected]"
},
"stage_history": [
{
"id": "sp_mno345",
"stage_name": "Screening",
"stage_type": "default",
"status": "completed",
"started_at": "2026-01-20T09:15:00Z",
"completed_at": "2026-01-25T16:00:00Z"
},
{
"id": "sp_pqr678",
"stage_name": "Interview",
"stage_type": "interview",
"status": "in_progress",
"started_at": "2026-01-25T16:00:00Z",
"completed_at": null
}
],
"metafields": [
{
"key": "years_of_experience",
"label": "Years of Experience",
"field_type": "number",
"value": "8",
"source": "ai",
"confidence": 0.92,
"extracted_at": "2026-01-20T09:18:00Z",
"edited": false,
"original_value": null
},
{
"key": "salary_expectation",
"label": "Salary Expectation",
"field_type": "number",
"value": "150000",
"source": "ai",
"confidence": 0.75,
"extracted_at": "2026-01-20T09:18:00Z",
"edited": true,
"original_value": "140000"
}
]
}
],
"pagination": { ... }
}
Datos de metafields
Scope requerido: candidates:read
Los valores de los metafields contienen datos personales del candidato extraídos por la IA, así que el array metafields está protegido por el scope candidates:read —el mismo que protege el bloque candidate— y no por applications:read. Un token con applications:read pero sin candidates:read recibe las respuestas de candidaturas sin ninguna clave metafields. La respuesta de ejemplo anterior es la que ve un token que tiene ambos scopes.
Cuando el token sí tiene candidates:read, cada candidatura incluye un array metafields que contiene campos de datos estructurados extraídos o introducidos para esa candidatura. Cada entrada tiene:
| Campo | Tipo | Descripción |
|---|---|---|
key |
string | Identificador legible por máquina del campo |
label |
string | Nombre visible legible por humanos |
field_type |
string | Tipo de dato: exactamente uno de estos nueve: text, textarea, number, date, select, boolean, url, rating, tags
|
value |
string | null | Valor actual (siempre serializado como string) |
source |
string | Cómo se definió el valor: exactamente uno de dos valores: manual (introducido o editado por una persona) o ai (extraído o definido por la IA) |
confidence |
float | null | Puntuación de confianza de la IA (0.0–1.0) cuando source es ai; null en caso contrario |
extracted_at |
timestamp | null | Cuándo se definió o extrajo el valor |
edited |
boolean |
true solo cuando un valor de origen IA se sobrescribió después con algo distinto de la extracción original de la IA (en la práctica, mediante una llamada MCP hiring_update_metafield_value). Las ediciones que hace una persona en la propia interfaz de Kit guardan source: manual y siempre indican edited: false. |
original_value |
string | null | El valor original extraído por la IA antes de las ediciones humanas; null si nunca se editó |
Dentro de un token que tiene candidates:read, el array metafields no se filtra además según el indicador de visibilidad managers_only. Cada metacampo definido en la oferta de empleo —incluidos los campos marcados como managers_only en la configuración de la oferta— se devuelve, independientemente del rol del propietario del token. Por tanto, candidates:read es el único control sobre la exposición de los metafields: si un campo contiene información sensible que no quieres exponer a una integración, no le concedas el scope candidates:read, ya que la API no aplica el filtrado de visibilidad para managers que sí se usa en otras partes de la aplicación. Conceder solo applications:read es seguro en este sentido: devuelve el historial de etapas y el estado de la candidatura sin los metafields.
Obtener una candidatura
GET /api/v1/applications/:id
Devuelve la misma estructura que un elemento de la respuesta de lista. Cuando el token tiene el scope candidates:read y la candidatura tiene un CV adjunto, la respuesta incluye metadatos del CV:
{
"id": "app_jkl012",
"resume_filename": "john_doe_resume.pdf",
"resume_content_type": "application/pdf",
"resume_byte_size": 245760,
...
}
Estos campos se omiten cuando no hay CV adjunto o cuando falta el scope candidates:read.
Descargar CV
Scopes requeridos: applications:read + candidates:read
GET /api/v1/applications/:application_id/resume
Descarga el CV del candidato para la candidatura indicada. Devuelve una redirección 302 a una URL firmada con tiempo limitado (válida durante 30 minutos).
Ejemplo:
curl -L -H "Authorization: Bearer YOUR_TOKEN" \
-o resume.pdf \
https://startupkit.app/api/v1/applications/app_jkl012/resume
Respuesta:
-
302 Found– Redirección a la URL de descarga firmada. Usa-L(seguir redirecciones) con curl. -
403 Forbidden– El token no tiene el scopeapplications:readocandidates:read. -
404 Not Found– Candidatura no encontrada, o sin CV adjunto.
Notas:
- Las URL firmadas expiran a los 30 minutos. Si expira, solicita una nueva.
- El encabezado
Content-Dispositionestá configurado comoattachment(inicia la descarga en el navegador). - Tipos de archivo de CV: PDF, DOC, DOCX (según lo subido por el candidato).
Ocultamiento de datos personales del candidato
Cuando un token tiene applications:read pero no candidates:read, los datos del candidato en las respuestas de candidaturas se ocultan y solo muestran el ID:
{
"candidate": {
"id": "cand_ghi789"
}
}
Los campos first_name, last_name y email no se incluyen. El array metafields y los campos de metadatos del CV se omiten por completo por la misma regla, ya que también pueden contener datos personales del candidato. Esto permite que las integraciones de portales de empleo rastreen el estado de las candidaturas sin exponer la información de contacto de los candidatos.
Diagnóstico de problemas
Problemas de autenticación
401 Unauthorized
Si recibes una respuesta 401, verifica:
- Formato del token: Asegúrate de que el token coincida con el valor que se muestra una sola vez al crearlo (cadena base58 de 24 caracteres). Si ya no lo tienes, revoca el token y crea uno nuevo: Kit solo guarda un hash y no puede volver a mostrarlo.
-
Encabezado Authorization: Debe ser
Authorization: Bearer TOKENoAuthorization: token TOKEN - El token sigue existiendo: Confirma que el token siga apareciendo en Settings > API: un token revocado se elimina, no se desactiva
- Membresía en la cuenta: Confirma que el propietario del token sigue siendo miembro de la cuenta
403 Forbidden
Cuando recibes un 403, el token es válido pero no tiene el scope requerido:
-
Verifica los scopes requeridos: Cada endpoint documenta su scope requerido (p. ej.,
job_postings:read) - Añade los scopes faltantes: Edita el token en Settings > API y añade el scope necesario
-
Formato del scope: Debe coincidir exactamente:
job_postings:read, nojob_postingsniread
Problemas de acceso a datos
Array data vacío en las respuestas
Si la API devuelve {"data": [], "pagination": {...}} pero esperas resultados:
- Aislamiento por cuenta: Los tokens de API solo ven datos de la cuenta a la que están vinculados
- Verifica la cuenta: Comprueba a qué cuenta pertenece el token en Settings > API
- Acceso entre cuentas: Los tokens no pueden acceder a datos de otras cuentas, incluso si el usuario es miembro de ellas
Los datos personales del candidato están ocultos
Si solo ves {"candidate": {"id": "cand_..."}} sin nombre ni email:
-
Comportamiento esperado: Esto ocurre cuando el token tiene
applications:readpero nocandidates:read -
Añade el scope: Para ver los detalles completos del candidato, añade el scope
candidates:reada tu token - Privacidad por diseño: Esto permite que las integraciones rastreen candidaturas sin exponer información de contacto
Falta metafields en las respuestas de candidaturas
El array metafields está protegido por el scope candidates:read, no por applications:read, porque los valores de los metafields pueden contener datos personales del candidato extraídos por la IA. Si las respuestas no traen ninguna clave metafields, al token le falta candidates:read. Añade ese scope solo si la integración también debe ver nombres, correos y CV de los candidatos: ambas cosas van juntas.
Problemas de consultas y filtros
404 al buscar recursos específicos
-
Usa IDs con prefijo: Las ofertas de empleo usan el formato
job_abc123, no IDs de base de datos como42 - Sensibilidad a mayúsculas: Los IDs con prefijo distinguen entre mayúsculas y minúsculas
- Alcance de la cuenta: El recurso debe pertenecer a la cuenta del token
Los parámetros de filtro no funcionan
-
Valores de estado válidos:
- Ofertas de empleo:
draft,published,paused,closed(distingue mayúsculas) - Candidaturas:
active,rejected(distingue mayúsculas)
- Ofertas de empleo:
-
Formato de parámetros: Usa
?status=published, no?status=Publishedni?filter[status]=published
Preguntas frecuentes
¿Cómo funciona la paginación?
Todos los endpoints de lista devuelven 20 elementos por página (fijo). Usa ?page=2 para la siguiente página. El objeto pagination muestra total_pages y total_count.
¿Los tokens expiran automáticamente?
No. No hay ninguna opción de expiración al crear un token, así que un token permanece válido hasta que:
- Se revoquen manualmente en Settings > API
- El usuario sea eliminado de la cuenta
- La cuenta sea eliminada
¿Debo usar un solo token para múltiples integraciones?
No. La buena práctica es un token por integración con los scopes mínimos. Esto facilita:
- Auditar qué integración está haciendo solicitudes (a través de
last_used_at) - Revocar el acceso a una integración específica sin afectar a las demás
- Otorgar permisos diferentes a distintos sistemas
¿Cuál es la relación entre los webhooks y la API?
Los webhooks te notifican de eventos (nueva candidatura, cambio de etapa). La API te permite consultar el estado actual. Usa los webhooks para activar tu sistema y luego usa la API para obtener los detalles completos.
¿Puedo obtener más de 20 elementos por página?
No. El tamaño de página está fijado en 20 elementos para garantizar un rendimiento estable. Usa el parámetro page para iterar sobre todos los resultados.
Lista de comprobación rápida
- Crea un token de API en Settings > API – cada token está vinculado a esa cuenta
- Asigna solo los scopes que tu integración necesita (buena práctica: un token por integración con los scopes mínimos)
-
No concedas
candidates:readsalvo que la integración deba ver los datos personales del candidato: ese scope también controla el arraymetafieldsy el acceso al CV -
Incluye
Authorization: Bearer TOKENen cada solicitud -
Usa IDs con prefijo (p. ej.,
job_abc123) en las URLs, no IDs de base de datos -
Maneja las respuestas
400y422para errores de validación -
Maneja las respuestas
401– puede que tu token haya sido revocado -
Maneja las respuestas
403– a tu token puede faltarle un scope requerido -
Usa el parámetro
pagepara paginar grandes conjuntos de resultados (20 elementos por página)