Crear
Crea uno o más contratos.
Descripcion general
Este endpoint permite crear uno o varios contratos dentro de tu organización. Acepta tanto un objeto como un arreglo de hasta 100 objetos, y la respuesta refleja la forma del cuerpo enviado. Un contrato es propiedad de la organización y se comparte entre las razones sociales que indiques en legal_entity_numbers mediante un puente (relación M:N). El campo contract_type corresponde a un código SAT c_TipoContrato (p. ej. "01" para indeterminado) y concept_numbers lista los conceptos de nómina extralegales que el contrato adjunta. Una solicitud única exitosa devuelve 201 con el contrato creado; una solicitud por lote devuelve 201 con { summary, created[] }.
Un contrato nuevo empieza con las prestaciones mínimas de ley: aguinaldo de 15 días, prima vacacional de 25 % y vacaciones según la tabla de ley.
Nota sobre
concept_numbers: la API todavía no documenta cómo listar los conceptos de nómina, así queconcept_numbersdebe usar números que ya conozcas, o puedes omitirlo (arreglo vacío).
Endpoint
https://app.integranom.com/api/v1/contractsSolicitud unica
Envía un objeto único con al menos legal_entity_numbers, name y contract_type. El contrato se crea activo por defecto y queda compartido por todas las razones sociales indicadas. Puedes omitir concept_numbers para crearlo sin conceptos adjuntos (arreglo vacío).
{
"legal_entity_numbers": [1, 2],
"name": "Indeterminado tiempo completo",
"contract_type": "01",
"concept_numbers": [7]
}Solicitud por lote (1–100 contratos)
Envía un arreglo de hasta 100 contratos en el mismo endpoint; el modo lote se activa al enviar un arreglo como cuerpo. El lote es atómico: si cualquier fila falla la validación, ningún contrato se crea y la respuesta lista los problemas por fila.
[
{ "legal_entity_numbers": [1], "name": "Indeterminado tiempo completo", "contract_type": "01" },
{ "legal_entity_numbers": [1, 2], "name": "Por obra determinada", "contract_type": "03" }
]Reglas de los campos
| Campo | Tipo | Requerido | Notas |
|---|---|---|---|
legal_entity_numbers | arreglo de enteros positivos | Sí | Al menos 1 elemento. Las razones sociales que comparten el contrato. |
name | string | Sí | 3–80 caracteres, sin contar espacios al inicio o al final, que no se guardan. Único por organización. |
contract_type | string | Sí | Código SAT c_TipoContrato (p. ej. "01"). |
description | string | No | ≤ 280 caracteres. |
concept_numbers | arreglo de enteros positivos | No | Por defecto []. Conceptos extralegales a adjuntar. |
concept_calculations | arreglo de objetos | No | El cálculo que el contrato fija para un concepto. Cada objeto lleva concept_number y calculation (captured, fixed_amount o formula). Con fixed_amount envía amount (monto por periodo, mayor a cero, hasta 999,999,999,999.99 y con hasta dos decimales); con formula envía formula (texto de hasta 2000 caracteres, validado contra el vocabulario de fórmulas). Un concepto que no listas conserva su propio cálculo. El concepto debe venir también en concept_numbers. Cada concepto aparece una sola vez. |
concept_values | arreglo de objetos | No | Formato anterior de concept_calculations, que se sigue aceptando. Cada objeto lleva concept_number y amount (monto por periodo, mayor a cero; se redondea a dos decimales) o rate_of_salary (fracción del salario del periodo, mayor a cero y hasta 1), nunca ambos: un monto se guarda como cálculo fixed_amount y una fracción como la fórmula var.sueldo_diario * var.dias_pagados * <fracción>. No se envía junto con concept_calculations. Si un concepto se repite, cuenta el último. |
Prestaciones
Todas son opcionales al crear: lo que omitas toma el valor de ley, y lo que envíes se valida con los mínimos de ley sobre el resultado. Sin ninguna prestación el contrato queda como el de ley (is_legal_default en true); con al menos una es personalizado (template_key en null). Las mismas claves sirven para editar.
| Campo | Tipo | Notas |
|---|---|---|
year_end_bonus_table | object[] | Renglones { years_from, years_to, days } con days ≥ 15. Empiezan en years_from 0, sin huecos ni traslapes, y solo el último tiene years_to en null. No se puede enviar junto con year_end_bonus_days. |
year_end_bonus_days | integer | Obsoleto: utiliza year_end_bonus_table. Un mismo aguinaldo, ≥ 15. Al editar solo se acepta si la tabla tiene un solo renglón. |
year_end_bonus_discounts_absences | booleano | Si las faltas injustificadas, los permisos y las vacaciones sin goce y las incapacidades por enfermedad general se descuentan de los días trabajados del aguinaldo y del finiquito. Por omisión es true; al editar, si lo omites se conserva el valor guardado. |
vacation_table | object[] | Misma forma que year_end_bonus_table, por años cumplidos. El renglón del primer año (years_from 0) debe tener days ≥ 12. |
vacation_premium_rate | número | Entre 0.25 y 1. |
vacation_premium_payment_mode | "on_days_taken" | "on_anniversary" | "not_calculated" | Cuándo se paga la prima vacacional en todo el contrato. |
sunday_premium_rate | número | Entre 0.25 y 1. |
food_voucher_enabled | boolean | Si es true, son obligatorios food_voucher_delivery_method y exactamente uno de food_voucher_rate o food_voucher_monthly_amount (> 0). |
food_voucher_rate | número | null | Entre 0 y 0.5. |
food_voucher_monthly_amount | número | null | Monto fijo mensual de vales. |
food_voucher_delivery_method | "card" | "cash" | null | Método de entrega. |
food_voucher_capped_at_imss_exempt_limit | boolean | Limita los vales del mes al tope exento del IMSS. |
food_voucher_grant_frequency | "every_run" | "monthly" | Con "every_run" cada nómina paga su parte del monto mensual; con "monthly" una sola nómina paga el mes completo. |
food_voucher_weekly_paying_run | "week_1" | "week_2" | "week_3" | "week_4" | "last_week" | null | Semana que paga los vales. Requerido con "monthly", igual que los tres siguientes; con "every_run" o con los vales inactivos se guardan como null. |
food_voucher_semimonthly_paying_run | "first_half" | "second_half" | null | Quincena que paga los vales. |
food_voucher_biweekly_paying_run | "period_1" | "period_2" | "last_period" | null | Catorcena que paga los vales. |
food_voucher_decennial_paying_run | "period_1" | "period_2" | "period_3" | null | Decena que paga los vales. |
savings_fund_enabled | boolean | Si es true, savings_fund_employee_rate (> 0) y savings_fund_employer_rate son obligatorios. |
savings_fund_employee_rate | número | null | Entre 0 y 0.13. |
savings_fund_employer_rate | número | null | Igual a la del trabajador. |
savings_fund_withdrawal_frequency | "once_a_year" | "twice_a_year" | "more_than_twice_a_year" | null | Requerida cuando el fondo está activo; null cuando está inactivo. |
Coherencia de conceptos
Cada número enviado en concept_numbers debe corresponder a un concepto activo y extralegal de tu organización, sin importar las razones sociales del contrato. Los conceptos de ley se calculan a cada empleado y nunca se adjuntan a un contrato. Si un número no cumple estas condiciones, la fila se rechaza con CONCEPT_NOT_FOUND.
Forma de las respuestas
201 — solicitud única
Cuando el cuerpo es un objeto, la respuesta es el objeto del contrato creado.
{
"contract_number": 4,
"name": "Indeterminado tiempo completo",
"contract_type": "01",
"description": null,
"is_active": true,
"sort_order": 0,
"legal_entity_numbers": [1, 2],
"concept_numbers": [7],
"created_at": "2026-01-01T00:00:00.000Z",
"updated_at": "2026-01-01T00:00:00.000Z"
}Cada contrato de la respuesta incluye además template_key, is_legal_default, concept_calculations, concept_values (los montos fijos y las fórmulas de fracción del salario, en el formato anterior) y todas las prestaciones, más year_end_bonus_discounts_absences y year_end_bonus_days, que es null cuando la tabla de aguinaldo cambia con la antigüedad.
201 — solicitud por lote
Cuando el cuerpo es un arreglo, la respuesta envuelve los resultados en summary + created[].
{
"summary": { "total": 2, "created": 2 },
"created": [
{ "contract_number": 4, "...": "..." },
{ "contract_number": 5, "...": "..." }
]
}Errores
400 — validación fallida (solicitud única)
Para fallas de validación de campos, errors es un objeto indexado por campo:
{
"message": "Validation failed.",
"errors": { "name": ["El nombre debe tener entre 3 y 80 caracteres."] }
}400 — validación fallida (solicitud por lote)
Reporte por fila. Ningún contrato del lote fue creado.
{
"message": "Validation failed for 1 contract(s). No contracts were created.",
"summary": { "total": 2, "with_errors": 1 },
"errors": [
{
"index": 0,
"contract": { "name": "Indeterminado tiempo completo" },
"issues": [
{
"field": "concept_numbers",
"code": "CONCEPT_NOT_FOUND",
"message": "No se encontró un concepto extralegal activo con el número '7' en la organización."
}
]
}
]
}index— posición 0-based de la fila que falló dentro del arreglo enviado.contract.name— extraído de tu entrada para que identifiques la fila sin contar.issues[]— todos los problemas de esa fila listados juntos para corregirlos antes de reintentar.
409 — nombre duplicado
Se devuelve cuando el name ya existe en la organización:
{
"message": "A contract with this 'name' already exists in this organization."
}Vocabulario de códigos de error
Estos códigos aparecen dentro de issues[].code (o como code en el cuerpo de error en single) y son estables — tu cliente puede ramificar lógica con base en ellos:
| Codigo | Significado |
|---|---|
INVALID_INPUT | Falla genérica de validación de campos (formato, longitud, tipo). |
LEGAL_ENTITY_NOT_FOUND | Un legal_entity_number no existe en esta organización. |
CONCEPT_NOT_FOUND | Un concept_number no corresponde a un concepto extralegal activo. |
CONCEPT_CALCULATION_NOT_ALLOWED | Un elemento de concept_calculations apunta a un concepto que no viene en concept_numbers, o a uno que calcula la plataforma o un crédito. |
CONCEPT_VALUE_NOT_ALLOWED | Un elemento de concept_values apunta a un concepto que no viene en concept_numbers. |
INVALID_BENEFITS | Las prestaciones combinadas no cumplen sus reglas: vacaciones del primer año ≥ 12 días, la tabla completa sin huecos, y la coherencia de vales de despensa y fondo de ahorro. Un valor fuera de rango en un solo campo responde con errors por campo, sin este código. |

