> ## Documentation Index
> Fetch the complete documentation index at: https://firespark.cloud/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Crear o actualizar fulfillments

> Crea o actualiza hasta 100 opciones de fulfillment por id externo en una sola solicitud.

Envía un arreglo de opciones de fulfillment. Cada ítem crea la opción cuando su `id` no existe, o la actualiza cuando ya existe. Envía solo los campos a cambiar. El `id` va en cada ítem y no se puede renombrar aquí.

> [!NOTE]
>
> Requiere un access token con el scope `fulfillment:write`. Ver
> [Token](https://firespark.cloud/docs/es/integrations-api/oauth/token/post.md) para obtenerlo.

Un ítem con solo `id` refresca `updated_at` si la opción ya existe. Un `id` nuevo requiere `name` y `type`. Si omites `status` queda `ACTIVE` y si omites `channel_ids` queda `[]`.

## Solicitud

```bash theme={null}
curl -X PUT "https://firespark.cloud/api/integrations/v1/fulfillment" \
  -H "Authorization: Bearer ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '[
    {
      "id": "delivery",
      "name": { "es": "Domicilio" },
      "channel_ids": ["app", "web"],
      "status": "ACTIVE"
    },
    {
      "id": "pickup",
      "name": { "es": "Retiro" },
      "type": "PICKUP"
    }
  ]'
```

## Cuerpo

Un arreglo de 1 a 100 ítems. Cada ítem acepta estos campos:

| Campo | Tipo | Descripción |
| - | - | - |
| `id` | string | Id externo del fulfillment. Obligatorio. Caracteres alfanuméricos, `_` y `-`, de 1 a 64 caracteres. |
| `name` | object | Mapa de locale (`es`, `en-US`, …) a un string de 1–1024 caracteres. Reemplaza el mapa completo. Si se envía, hace falta al menos un locale. |
| `type` | string | Código de tipo, 1–100 caracteres. Un valor por comercio. Se permiten `DELIVERY`, `PICKUP` y códigos propios. |
| `channel_ids` | string\[] | Ids externos de canal. Reemplaza el arreglo completo. `[]` lo vacía. Cada id es alfanumérico, `_` o `-`, de 1–64 caracteres. |
| `status` | string | `ACTIVE` o `INACTIVE`. |

Si se envían `workflow`, `preset`, `labels`, `uid`, `merchant_id` o `env`, se ignoran.

## Respuesta

Zod valida el cuerpo completo antes de guardar. Si algún campo es inválido, responde HTTP `400` y no se guarda ningún ítem. Cuando el cuerpo es válido, los ítems se guardan uno tras otro. El HTTP es `200` solo si todos se guardaron. Si un ítem posterior falla por una regla de negocio, el HTTP es el `status` de ese ítem (`422` o `500`).

| Campo | Descripción |
| - | - |
| `id` | El `id` del ítem. `null` si faltaba o no era válido. |
| `status` | `200` si se guardó. `422` si `name` o `type` ya lo usa otra opción, o si un `id` nuevo llegó sin `name` y `type`. `500` por un fallo inesperado. |
| `data` | La opción guardada, con `labels`. Solo cuando `status` es `200`. |
| `error` | Mensaje de error. Solo cuando `status` no es `200`. |
| `details` | Detalle del error a nivel de ítem. |

```json Success theme={null}
{
  "data": [
    {
      "id": "delivery",
      "status": 200,
      "data": {
        "id": "delivery",
        "uid": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
        "merchant_id": "c1d2e3f4-a5b6-7890-cdef-123456789abc",
        "name": { "es": "Domicilio" },
        "type": "DELIVERY",
        "status": "ACTIVE",
        "channel_ids": ["app", "web"],
        "labels": []
      }
    },
    {
      "id": "pickup",
      "status": 422,
      "error": "Fulfillment type already exists",
      "details": null
    }
  ]
}
```

```json Campos inválidos theme={null}
{
  "error": "Invalid fields",
  "details": {
    "0.type": "Invalid option"
  }
}
```

## Errores

| Estado | Descripción |
| - | - |
| `400` | El cuerpo no es un arreglo de 1 a 100 ítems, o algún ítem tiene campos inválidos. No se guarda ningún ítem. |
| `401` | Access token ausente o inválido. |
| `403` | El token no incluye el scope `fulfillment:write`. |


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.