Skip to navigation

Crear turnos

Cómo crear los turnos de Asistencia desde tu sistema de RR. HH. o de planificación, fijos o rotativos, con sus descansos y tolerancias.

Si tu organización usa Asistencia en Crehana y los horarios viven en otro sistema, podés crear los turnos por API en vez de cargarlos a mano. Un turno creado por API es igual a uno creado desde Crehana: se puede asignar, las marcas de la gente se comparan contra su horario y la puntualidad se clasifica con sus tolerancias.

POST /api/v5/rest/org/{organization_slug}/time/attendance/shifts/

La referencia completa está en Create Shift.

Antes de empezar

  • Credenciales de administrador. Las credenciales de la API tienen que ser de una cuenta ADMIN de la organización. Con otras, la respuesta es 403.
  • Elegí el tipo de turno. Un turno fijo (NON_ROTATING) repite el mismo horario todas las semanas: lunes a viernes de 9 a 18, por ejemplo. Un turno rotativo (ROTATING) repite un patrón de bloques de trabajo y descanso sin mirar el día de la semana: 4 días de trabajo y 2 de descanso, por ejemplo.
  • Un nombre por turno. El nombre es único entre los turnos activos de la organización. Si ya existe, la respuesta es 409 con el id del turno que lo tiene.
  • Guardá los ids de la respuesta. El id del turno es el shift_id que pide Asignar turnos; en un rotativo, el id de cada ciclo es el start_cycle_id.

Un turno fijo de punta a punta

curl -X POST "https://www.crehana.com/api/v5/rest/org/acme/time/attendance/shifts/" \
-H "Api-Key: $API_KEY" \
-H "Secret-Access: $SECRET_ACCESS" \
-H "Content-Type: application/json" \
-d '{
"shift_type": "NON_ROTATING",
"name": "Mañana 09-18",
"description": "Oficina, lunes a viernes",
"color": "BLUE",
"weekly_schedule": [
{ "day": "MONDAY", "entry_time": "09:00", "exit_time": "18:00" },
{ "day": "TUESDAY", "entry_time": "09:00", "exit_time": "18:00" },
{ "day": "WEDNESDAY", "entry_time": "09:00", "exit_time": "18:00" },
{ "day": "THURSDAY", "entry_time": "09:00", "exit_time": "18:00" },
{ "day": "FRIDAY", "entry_time": "09:00", "exit_time": "18:00" }
]
}'
{
"success": true,
"message": "Shift created.",
"code": "attendance-005",
"datetime": "2026-10-13T09:00:03.120394",
"data": {
"id": "4ed0f6c7-a2a7-4de7-90cd-ae8cf26aabb1",
"name": "Mañana 09-18",
"shift_type": "NON_ROTATING",
"description": "Oficina, lunes a viernes",
"color": "BLUE",
"is_active": true,
"created_at": "2026-10-13T14:00:03",
"break_counts_as_worked": false,
"mark_association_window_hours": 2,
"edit_worked_minutes": false,
"break_config": null,
"tolerance_config": null,
"weekly_schedule": [
{
"day": "MONDAY",
"is_working_day": true,
"entry_time": "09:00",
"exit_time": "18:00",
"worked_minutes": 540,
"break_time_slots": null,
"break_duration_minutes": null
},
{ "day": "TUESDAY", "...": "..." },
{ "day": "SATURDAY", "is_working_day": false, "entry_time": null, "exit_time": null, "worked_minutes": null, "break_time_slots": null, "break_duration_minutes": null },
{ "day": "SUNDAY", "is_working_day": false, "...": "..." }
],
"apply_same_schedule_to_all_working_days": false,
"total_working_days": 5,
"total_worked_hours_per_week": 45.0,
"cycles": null
}
}

El 201 confirma que el turno existe. data.id es el shift_id para asignarlo. Los días que no mandaste (sábado y domingo) vuelven como días de descanso, y worked_minutes lo calculó Crehana a partir del horario.

Un turno rotativo

curl -X POST "https://www.crehana.com/api/v5/rest/org/acme/time/attendance/shifts/" \
-H "Api-Key: $API_KEY" \
-H "Secret-Access: $SECRET_ACCESS" \
-H "Content-Type: application/json" \
-d '{
"shift_type": "ROTATING",
"name": "Planta 4x2",
"cycles": [
{ "cycle_type": "WORK", "duration_days": 4, "entry_time": "07:00", "exit_time": "19:00" },
{ "cycle_type": "REST", "duration_days": 2 }
]
}'
{
"success": true,
"message": "Shift created.",
"code": "attendance-005",
"datetime": "2026-10-13T09:01:10.004210",
"data": {
"id": "78e62b28-6a19-4d8c-b7f4-0c2e9a5d1f33",
"name": "Planta 4x2",
"shift_type": "ROTATING",
"is_active": true,
"cycles": [
{
"id": "1d8f297b-f186-46bb-9952-ac25304806ac",
"cycle_order": 1,
"cycle_type": "WORK",
"duration_days": 4,
"entry_time": "07:00",
"exit_time": "19:00",
"worked_minutes_per_day": 720,
"break_time_slots": null,
"break_duration_minutes": null
},
{
"id": "9c0a5e2d-1b7e-4a3f-8d2c-5e6f7a8b9c0d",
"cycle_order": 2,
"cycle_type": "REST",
"duration_days": 2,
"entry_time": null,
"exit_time": null,
"worked_minutes_per_day": null,
"break_time_slots": null,
"break_duration_minutes": null
}
],
"total_pattern_days": 6,
"total_work_days": 4,
"total_rest_days": 2,
"number_of_cycles": 2,
"weekly_schedule": null,
"...": "..."
}
}

El patrón se repite en ese orden: 4 días de trabajo, 2 de descanso, 4 de trabajo… Al asignar el turno, start_cycle_id dice con qué ciclo arranca la persona el día start_date. Guardá el id de cada ciclo.

Los campos

shift_type

NON_ROTATING o ROTATING. Decide qué más va en el cuerpo: un turno fijo lleva weekly_schedule; uno rotativo lleva cycles. Mandar el otro campo es 422.

name y description

name hasta 150 caracteres, único entre los turnos activos de la organización. description es opcional, hasta 250. Los dos se guardan sin espacios al principio ni al final.

weekly_schedule (turno fijo)

Una entrada por día de la semana, con day (MONDAY a SUNDAY), entry_time y exit_time. Mandá solo los días laborables: los que faltan quedan como días de descanso. Un día repetido es 422; ningún día laborable es 400.

Si preferís listar los 7, un día de descanso se manda con "is_working_day": false y sin horario.

Con apply_same_schedule_to_all_working_days: true todos los días laborables tienen que llevar el mismo horario y el mismo descanso; después, Crehana los mantiene iguales cuando se edita uno.

cycles (turno rotativo)

Los bloques del patrón, en orden. Cada uno lleva cycle_type (WORK o REST) y duration_days (al menos 1). Un bloque WORK lleva entry_time y exit_time; un bloque REST no lleva horario. Hacen falta al menos un WORK y un REST.

Las horas

Siempre en HH:MM de 24 horas: 09:00, 18:30, 23:00. Una salida menor que la entrada es un turno nocturno: 22:00 a 06:00 termina al día siguiente. Si un turno nocturno pisa la entrada del día siguiente (sale a las 06:00 del martes y el martes entra a las 05:00), la respuesta es 400.

Descansos: break_config

Sin break_config el turno no tiene descanso y ningún día ni ciclo puede llevar datos de descanso. Con break_config se define cómo es el descanso a nivel del turno, y en cada día o ciclo laboral va el descanso en sí:

break_typeQué va en cada día o ciclo laboralEjemplo
FIXED_SCHEDULEbreak_time_slots: uno o más tramos con break_label (LUNCH, COFFEE_BREAK, REST), start_time y end_time, dentro del horario y sin solaparseAlmuerzo 13:00-14:00
FIXED_DURATIONbreak_duration_minutes: cuánto dura el descanso60 minutos, a cualquier hora

deduction_method dice cómo se descuenta del tiempo trabajado: REAL descuenta el descanso que la persona marcó; FIXED descuenta siempre el configurado. Con break_counts_as_worked: true el descanso cuenta como trabajado y no se descuenta.

{
"shift_type": "NON_ROTATING",
"name": "Mañana con almuerzo",
"break_config": { "break_type": "FIXED_SCHEDULE", "deduction_method": "REAL" },
"weekly_schedule": [
{
"day": "MONDAY",
"entry_time": "09:00",
"exit_time": "18:00",
"break_time_slots": [
{ "break_label": "LUNCH", "start_time": "13:00", "end_time": "14:00" }
]
}
]
}

Tolerancias: tolerance_config

Minutos de gracia antes y después del horario, cada uno con los eventos a los que aplica (ENTRY, EXIT, BREAK). Una entrada dentro de la tolerancia posterior no cuenta como tardanza.

{
"tolerance_config": {
"before_minutes": 10,
"before_applicable_events": ["ENTRY"],
"after_minutes": 15,
"after_applicable_events": ["ENTRY", "EXIT"]
}
}

BREAK solo se puede usar con un break_config. Los minutos no pueden ser negativos y cada lado necesita al menos un evento.

worked_minutes y edit_worked_minutes

Por defecto Crehana calcula los minutos trabajados de cada día o ciclo: la franja entre entrada y salida menos el descanso configurado (o la franja completa con break_counts_as_worked). Si mandás worked_minutes (o worked_minutes_per_day en un ciclo) sin edit_worked_minutes: true, se ignora.

Con edit_worked_minutes: true vos fijás el valor y pasa a ser obligatorio en cada día o ciclo laboral.

mark_association_window_hours

Cuántas horas antes de la entrada y después de la salida una marca todavía pertenece a este turno. Entre 1 y 12; por defecto 2. Subilo si tu gente suele marcar con mucha anticipación.

color

El color del turno en el calendario de Crehana: GRAY (por defecto), PURPLE, BLUE, GREEN, BEIGE, ORANGE, PINK o YELLOW_GREEN.

Reintentos: el nombre protege contra duplicados

Si se corta la conexión y no sabés si el turno se creó, reenviá la misma creación. Si ya existe un turno activo con ese nombre, la respuesta es 409 con detail.code: SHIFT_NAME_ALREADY_EXISTS y detail.shift_id con el id del turno existente: guardalo y seguí con la asignación. No se crea un duplicado.

{
"detail": {
"errors": ["An active shift with this name already exists in the organization."],
"code": "SHIFT_NAME_ALREADY_EXISTS",
"shift_id": "4ed0f6c7-a2a7-4de7-90cd-ae8cf26aabb1"
}
}

Ojo: el 409 dice que el nombre está tomado, no que el turno existente tenga el mismo horario. Si cambiaste el horario, editalo desde Crehana o creá el turno con otro nombre.

Respuestas

StatusCuándoQué hacer
201Turno creadoGuardar data.id y, en un rotativo, el id de cada ciclo
400El horario rompe una regla de Asistencia, o el organization_slug no existeVer detail.errors: dice la regla. Reintentar igual no sirve
401Faltan las credenciales o no son válidasRevisar los dos headers
403Las credenciales no administran esa organizaciónUsar credenciales de un admin de la organización
409Ya hay un turno activo con ese nombreUsar detail.shift_id, o cambiar el nombre
422El cuerpo no cumple el esquema (falta un campo, una hora que no es HH:MM, un día repetido, un valor fuera del vocabulario, un campo desconocido)Corregir el campo que indica detail[].loc
500Error nuestroReintentar con backoff
detail.codeStatusSignifica
VALIDATION_ERROR400Regla del turno: ningún día laborable, entrada igual a salida, un nocturno que pisa el día siguiente, descanso fuera del horario o solapado, BREAK en la tolerancia sin descanso, rotativo sin WORK o sin REST, REST con horario, edit_worked_minutes sin minutos
SHIFT_NAME_ALREADY_EXISTS409Ya hay un turno activo con ese nombre; detail.shift_id trae su id
FORBIDDEN403Las credenciales no administran la organización

Los textos de errors son para un log o para una persona: programá contra el status y, si hace falta, contra detail.code. Más en Errores.

Lo que la API no hace (todavía)

  • No lista los turnos ni sus ciclos. Guardá los ids de la respuesta.
  • No edita, desactiva ni elimina turnos: eso se hace desde Crehana.

Lista de verificación

  • Las credenciales son de un ADMIN de la organización.
  • shift_type es NON_ROTATING con weekly_schedule, o ROTATING con cycles.
  • Las horas van en HH:MM y sabés que una salida menor que la entrada es un turno nocturno.
  • Si hay descanso, está break_config y cada día o ciclo laboral trae su tramo o su duración.
  • Guardás data.id y, en un rotativo, el id de cada ciclo para asignar el turno.
  • Tu integración trata el 409 como “ya existe” y usa detail.shift_id.
  • Los 500 y los cortes de red se reintentan. Los 4xx se registran y no se reintentan.