# Crear (/docs/developers/api-reference/contracts/create)

## Descripcion general [#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í que `concept_numbers` debe usar números que ya conozcas, o puedes omitirlo (arreglo vacío).

## Endpoint [#endpoint]

`POST https://app.integranom.com/api/v1/contracts`

## Solicitud unica [#solicitud-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).

```json
{
  "legal_entity_numbers": [1, 2],
  "name": "Indeterminado tiempo completo",
  "contract_type": "01",
  "concept_numbers": [7]
}
```

**cURL**

```bash
curl -X POST "https://app.integranom.com/api/v1/contracts" \
-H "Authorization: Bearer TU_API_KEY" \
-H "Content-Type: application/json" \
-d '{
  "legal_entity_numbers": [1, 2],
  "name": "Indeterminado tiempo completo",
  "contract_type": "01",
  "concept_numbers": [7]
}'
```

**Respuesta**

```json
{
"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"
}
```

## Solicitud por lote (1–100 contratos) [#solicitud-por-lote-1100-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.

```json
[
  { "legal_entity_numbers": [1], "name": "Indeterminado tiempo completo", "contract_type": "01" },
  { "legal_entity_numbers": [1, 2], "name": "Por obra determinada", "contract_type": "03" }
]
```

**cURL**

```bash
curl -X POST "https://app.integranom.com/api/v1/contracts" \
-H "Authorization: Bearer TU_API_KEY" \
-H "Content-Type: application/json" \
-d '[
  { "legal_entity_numbers": [1], "name": "Indeterminado tiempo completo", "contract_type": "01" },
  { "legal_entity_numbers": [1, 2], "name": "Por obra determinada", "contract_type": "03" }
]'
```

**Respuesta**

```json
{
"summary": { "total": 2, "created": 2 },
"created": [
  {
    "contract_number": 4,
    "name": "Indeterminado tiempo completo",
    "contract_type": "01",
    "description": null,
    "is_active": true,
    "sort_order": 0,
    "legal_entity_numbers": [1],
    "concept_numbers": [],
    "created_at": "2026-01-01T00:00:00.000Z",
    "updated_at": "2026-01-01T00:00:00.000Z"
  },
  {
    "contract_number": 5,
    "name": "Por obra determinada",
    "contract_type": "03",
    "description": null,
    "is_active": true,
    "sort_order": 0,
    "legal_entity_numbers": [1, 2],
    "concept_numbers": [],
    "created_at": "2026-01-01T00:00:00.000Z",
    "updated_at": "2026-01-01T00:00:00.000Z"
  }
]
}
```

## Reglas de los campos [#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 [#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](/docs/developers/api-reference/contracts/edit).

| 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](/docs/developers/api-reference/contracts/edit) 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](/docs/developers/api-reference/contracts/edit), 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 [#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 [#forma-de-las-respuestas]

### `201` — solicitud única [#201--solicitud-única]

Cuando el cuerpo es **un objeto**, la respuesta es el objeto del contrato creado.

```json
{
  "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](/docs/developers/api-reference/contracts/create#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 [#201--solicitud-por-lote]

Cuando el cuerpo es **un arreglo**, la respuesta envuelve los resultados en `summary` + `created[]`.

```json
{
  "summary": { "total": 2, "created": 2 },
  "created": [
    { "contract_number": 4, "...": "..." },
    { "contract_number": 5, "...": "..." }
  ]
}
```

## Errores [#errores]

### `400` — validación fallida (solicitud única) [#400--validación-fallida-solicitud-única]

Para fallas de validación de campos, `errors` es un objeto indexado por campo:

```json
{
  "message": "Validation failed.",
  "errors": { "name": ["El nombre debe tener entre 3 y 80 caracteres."] }
}
```

### `400` — validación fallida (solicitud por lote) [#400--validación-fallida-solicitud-por-lote]

Reporte por fila. **Ningún** contrato del lote fue creado.

```json
{
  "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 [#409--nombre-duplicado]

Se devuelve cuando el `name` ya existe en la organización:

```json
{
  "message": "A contract with this 'name' already exists in this organization."
}
```

## Vocabulario de códigos de error [#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. |