Publicar un puesto desde tu propio sistema
Actualizado
Si tus puestos ya viven en otro sitio —un ATS, una herramienta interna, una hoja de cálculo que alguien custodia— así es como llegan a Orbit sin que nadie los vuelva a teclear.
Qué necesitas
Una clave con el permiso de leer y gestionar empleos, creada en Orbit → Configuración → Claves de API. Una clave de solo lectura responde 403 en todas las peticiones de esta página, a propósito: publicar pone un anuncio delante del público, y la clave que alimenta una página de empleo no debería además poder publicar anuncios.
export ORBIT_API_KEY="…una clave con leer y gestionar empleos…"
La forma: dos llamadas
Crear y publicar están separados.
POST /v1/jobscrea un borrador. Nada es público.POST /v1/jobs/{jobId}/publishlo pone en marcha. Publicar es gratis.
Es deliberado. Publicar pone el puesto en tu página de vacante alojada, permite que la gente se postule y lo incorpora al feed de personas que buscan empleo en Wipperoz. Una sola llamada que hiciera todo eso significaría que una clave en un bucle mal configurado publica anuncios antes de que nadie mire un log.
También deja a una persona en el circuito cuando la quieres: crea los borradores desde tu sistema y deja que alguien de selección los publique desde Orbit después de leerlos. Si no la quieres, llama tú a publish. Ambas cosas están soportadas; la decisión es tuya, no nuestra.
Avisos gestionados
Un anuncio publicado recibe candidaturas y las lista en el orden en que llegaron. Gestionar un aviso es el servicio de pago de Orbit por encima de eso: sus candidatos se ordenan, se busca en tu pool de candidatos y puedes evaluarlos, desde Orbit o por la API (consulta Evaluar a una persona candidata). Alguien de selección lo activa aviso por aviso, en Orbit —el permiso leer y gestionar empleos crea, edita, publica y cierra puestos, y no gestiona avisos—.
Hay una sola cosa que llega a esta API: el título y la categoría de un aviso gestionado están bloqueados, como explica Editar un puesto más abajo.
Crear el borrador
curl -X POST https://api.wipperoz.com/v1/jobs \
-H "Authorization: Bearer $ORBIT_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: ats-job-4471" \
-d '{
"title": "Ingeniera de Backend Senior",
"category": "engineering",
"description": "<p>Responsable de los servicios detrás de nuestro proceso de contratación.</p>",
"responsibilities": ["Diseñar y desplegar servicios en AWS Lambda y DynamoDB"],
"skills": [
{"name": "TypeScript", "level": "required"},
{"name": "DynamoDB", "level": "nice_to_have"}
],
"employmentType": "full_time",
"contractType": "permanent",
"salary": {"min": 150000, "max": 180000, "currency": "AUD", "period": "year"},
"location": {"country": "AU", "state": "NSW", "city": "Sydney", "remotePolicy": "hybrid"}
}'
{
"job": {
"id": "job_01J9A0B1C2EXAMPLE",
"status": "draft",
"title": "Ingeniera de Backend Senior",
"category": "engineering",
"applyLink": "https://www.wipperoz.com/es/apply/job_01J9A0B1C2EXAMPLE?src=careers&account=acc_01J8Z3EXAMPLE"
}
}
Guarda el id. Es con lo que después publicas, editas y cierras el puesto, y es el identificador que deberías guardar junto al puesto en tu propio sistema.
Aquí solo title es obligatorio. Para publicar hace falta además una description y, la primera vez que un puesto se pone en marcha, una category; nada más bloquea nunca la publicación. description acepta HTML, que es lo que produce el editor de Orbit; el texto plano también vale.
Categorías
category dice qué tipo de empleo es, a partir de una lista fija. Envía el valor; la etiqueta es lo que muestra Orbit.
| Valor | En Orbit aparece como |
|---|---|
engineering |
Ingeniería |
data |
Datos |
product |
Producto |
design |
Diseño |
marketing |
Marketing |
sales |
Ventas |
customer_success |
Éxito del cliente |
operations |
Operaciones |
finance |
Finanzas |
hr_people |
Recursos humanos y personas |
legal |
Legal |
healthcare |
Salud |
education |
Educación |
hospitality |
Hostelería |
retail |
Comercio minorista |
logistics |
Logística |
construction_trades |
Construcción y oficios |
manufacturing |
Fabricación |
admin_support |
Administración y soporte |
other |
Otra |
Cualquier otro valor responde 400 nombrando los que acepta. other es para el puesto que no encaja en ninguno.
Publicarlo
curl -X POST https://api.wipperoz.com/v1/jobs/job_01J9A0B1C2EXAMPLE/publish \
-H "Authorization: Bearer $ORBIT_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: ats-job-4471-publish" \
-d '{"expiryDays": 30}'
{
"job": {"id": "job_01J9A0B1C2EXAMPLE", "status": "published", "url": "…"},
"firstPublish": true
}
expiryDayses30,60,90onullpara sin caducidad.nulles el valor por defecto: un puesto que desaparece en una fecha que nadie eligió es peor que uno que se queda.firstPublishesfalsecuando el puesto ya había estado activo: un puesto cerrado que reabres conserva su URL.- Un puesto al que le falta algo que la publicación necesita responde
422con una listamissing, en lugar de un400impreciso:titleodescriptionen cualquier publicación, ycategoryen la primera.
Editar un puesto
curl -X PATCH https://api.wipperoz.com/v1/jobs/job_01J9A0B1C2EXAMPLE \
-H "Authorization: Bearer $ORBIT_API_KEY" \
-H "Content-Type: application/json" \
-d '{"salary": {"min": 170000, "max": 200000, "currency": "AUD", "period": "year"}}'
Envía solo los campos que cambias; el resto se queda como está.
En un borrador el cambio se aplica al momento. En un puesto publicado las palabras nuevas quedan en espera en lugar de sustituir lo que la gente está leyendo ahora mismo, y la respuesta lo dice:
{"job": {"…": "…tal y como sigue leyéndose hoy"}, "pendingRevision": true}
Quienes se postulan lo hacen sobre las palabras que hay en la página, y cambiarlas por debajo de una candidatura abierta cambia lo que alguien aceptó después de que lo aceptara. Así que alguien de selección promueve la edición en Orbit, o la promueves tú en la misma llamada:
-d '{"salary": {...}, "publishEdits": true}'
Dos campos se saltan la cola y se aplican siempre al momento: expiresAt y maxMatches. Ambos existen para detener algo, y dejar “para en cincuenta coincidencias” a la espera de una promoción mantendría el emparejamiento en marcha más allá del límite que pusiste.
Un aviso gestionado conserva su título y su categoría
Cuando un aviso está gestionado, una edición que cambia su title o su category —directamente, o al promover una edición en espera con publishEdits— responde 409 y no cambia nada: ni el anuncio publicado ni lo que está en espera. La respuesta:
{
"error": {
"code": "managed_ad_locked",
"message": "This job is a managed ad, so its title cannot change. Post the new role as a new job.",
"requestId": "5f7a9c1e-2b3d-4e5f-8a9b-0c1d2e3f4a5b",
"fields": ["title"]
}
}
fields nombra lo que la edición habría cambiado. Volver a enviar el valor actual no es un cambio, así que una sincronización que envía todos los campos en cada pasada sigue funcionando.
El bloqueo es lo que impide que un aviso gestionado se renombre para convertirlo en otro empleo. Si el puesto en origen de verdad es otro empleo, publícalo como uno nuevo. Si el título nuevo simplemente es el correcto, alguien de selección deja de gestionar el aviso en Orbit, en la pestaña Configuración del puesto, y la edición pasa. Esta API no dice si un aviso está gestionado; este 409 es donde una integración se entera.
Cerrar un puesto
curl -X POST https://api.wipperoz.com/v1/jobs/job_01J9A0B1C2EXAMPLE/close \
-H "Authorization: Bearer $ORBIT_API_KEY"
La página alojada deja de aceptar candidaturas y dice que el puesto se ha cerrado, el puesto sale del feed de búsqueda de empleo, y el acceso a los CV que ese puesto concedió a tu equipo termina con él: la razón de ese acceso era una candidatura abierta. Cerrar un aviso gestionado también devuelve su espacio a tu plan.
Las candidaturas que ya recibiste se quedan, y se siguen pudiendo leer en Orbit. Cerrar no es borrar, y esta API no tiene borrado. Solo se puede cerrar un puesto publicado; un borrador o un puesto ya cerrado responden 409.
Reintentos que no publican dos veces
Envía una Idempotency-Key en cada POST. Cualquier valor que identifique la intención sirve; el identificador del puesto en tu propio sistema es ideal.
- Un reintento con la misma clave devuelve la primera respuesta, con
Idempotent-Replay: true. Ni un segundo anuncio ni una segunda publicación. - La misma clave con un cuerpo distinto responde
409. Dos intenciones con un mismo nombre son un error del que conviene enterarse. - Las claves se recuerdan 24 horas, por cuenta.
- Una petición rechazada libera su clave, así que un
422que resuelves añadiendo lo que faltaba se puede reintentar con la misma clave.
Sin la cabecera, dos creaciones idénticas hacen dos anuncios. Las redes dan timeout: envía la cabecera.
Mantener honesta una sincronización
- Guarda nuestro
idjunto a tu puesto. Es el único identificador estable. - Cierra lo que se cerró en origen. Un anuncio caducado le cuesta la tarde a alguien, lo que es peor que no tener anuncio.
- No vuelvas a crear en cada sincronización. Haz
PATCHsobre el puesto que ya creaste; unPOSTnuevo hace un segundo anuncio con una segunda URL. - Asigna una
categorya cada familia de puestos una sola vez, y envíala en cada creación. Un borrador sin ella no puede ponerse en marcha. - Trata
managed_ad_lockedcomo una decisión, no como un reintento. Reintentar el mismo cambio de nombre recibe el mismo409. - Límite de peticiones: 120 por minuto y clave. Cuarenta puestos son cuarenta llamadas, holgadamente dentro.
Referencia
Create a draft jobPOST /v1/jobs
Publish a jobPOST /v1/jobs/{jobId}/publish
Edit a jobPATCH /v1/jobs/{jobId}
Close a jobPOST /v1/jobs/{jobId}/close