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

# Enviar una operación por lotes

> Ejecuta una escritura o una exportación grande a través de muchos usuarios conectados en una sola llamada asíncrona. Envías un trabajo, consultas su estado y (para exportaciones y escrituras) descargas un archivo de resultados.

Ejecuta una operación por lotes a través de tus usuarios conectados en una sola llamada. A diferencia de las lecturas síncronas ([Obtener saldos por lotes](/get-bulk-balances), [Obtener transacciones por lotes](/get-bulk-transactions)), `submitBulkOperation` es **asíncrona**: valida la solicitud, crea un trabajo más un elemento por objetivo y regresa inmediatamente con un `jobId`. El trabajo se ejecuta en segundo plano — [sigue el trabajo](/track-bulk-job) para monitorear el progreso y recuperar los resultados.

Una sola mutación cubre las cinco operaciones, seleccionadas por el campo `operation`:

| `operation`                   | Qué hace                                                                  | Alcances de grant requeridos (por usuario)             |
| ----------------------------- | ------------------------------------------------------------------------- | ------------------------------------------------------ |
| `GET_BALANCES_EXPORT`         | Exporta saldos de efectivo a un archivo                                   | `LIST_PAYMENT`                                         |
| `GET_TRANSACTIONS_EXPORT`     | Exporta transacciones a un archivo                                        | `LIST_PAYMENT`, `LIST_PURCHASES`                       |
| `CREATE_TRANSFER`             | Mueve fondos entre el operador y/o usuarios conectados                    | `MAKE_PAYOUT_TRANSFER_SEND` (en el usuario **origen**) |
| `DEPOSIT_CASH_BALANCE`        | Fondea la cuenta de gasto de un usuario conectado desde su método de pago | `MAKE_DEPOSIT`                                         |
| `UPDATE_TRANSACTION_METADATA` | Edita memo / categoría en transacciones                                   | `LIST_PAYMENT`, `LIST_PURCHASES`                       |

## Requisitos

* `Authorization: Basic <API_KEY>`
* La capacidad de API por lotes en tu aplicación.
* Los alcances que necesita la operación elegida, otorgados por cada usuario objetivo (ver la tabla arriba). Un objetivo que los carezca se convierte en una falla por elemento — nunca falla todo el trabajo.

<Note>
  Cada envío **debe** llevar una clave de idempotencia — ya sea la entrada `idempotencyKey` o un encabezado `Idempotency-Key` (si se envían ambos, deben coincidir). Reenviar la misma clave devuelve el trabajo original en lugar de crear un duplicado, siempre que la solicitud sea idéntica byte por byte; una solicitud diferente bajo la misma clave se rechaza. No hay un valor predeterminado seguro para una escritura de fan-out, por lo que la clave es obligatoria.
</Note>

## Mutación

```graphql theme={null}
mutation SubmitBulkOperation($input: SubmitBulkOperationInput!) {
  submitBulkOperation(input: $input) {
    jobId
    status
    operation
    requestedTargetCount
    acceptedItemCount
    succeededItemCount
    failedItemCount
    skippedItemCount
    createdAt
  }
}
```

Un envío exitoso devuelve el trabajo en estado `QUEUED`. `acceptedItemCount` es cuántos objetivos se procesarán; `skippedItemCount` es cuántos fueron rechazados de entrada (por ejemplo, por faltar el alcance requerido). Consulta el trabajo para ver cómo se completan `succeededItemCount` / `failedItemCount` — ver [Seguir un trabajo por lotes](/track-bulk-job).

## Variables — exportar saldos / transacciones

`exportOptions` delimita la ventana (por defecto los últimos 90 días; `GET_BALANCES_EXPORT` es una instantánea en un punto en el tiempo e ignora la ventana). Usa [selección de objetivos](/bulk-api#selecting-target-users) como las lecturas.

```json theme={null}
{
  "input": {
    "operation": "GET_TRANSACTIONS_EXPORT",
    "idempotencyKey": "export-2024-06-01-a",
    "targetSpec": { "mode": "ALL_CONNECTED" },
    "exportOptions": {
      "createdGte": "2024-05-01T00:00:00Z",
      "createdLte": "2024-06-01T00:00:00Z",
      "includeMetadata": false
    }
  }
}
```

## Variables — crear transferencia

Cada elemento nombra su propio endpoint `from` y `to`, por lo que un trabajo puede mezclar direcciones: operador→usuario, usuario→operador y usuario→usuario. Un endpoint es **o bien** `{ "operator": true }` (la cuenta de pagos de tu aplicación) **o bien** `{ "externalReferenceId": "…" }` (un usuario conectado). `from` y `to` deben ser distintos. El grant del usuario **origen** debe permitir `MAKE_PAYOUT_TRANSFER_SEND`; el destino solo necesita estar conectado. Una transferencia permanece dentro de un mismo banco patrocinador. `targetSpec` no es requerido para transferencias — los participantes se toman de los elementos.

```json theme={null}
{
  "input": {
    "operation": "CREATE_TRANSFER",
    "idempotencyKey": "payouts-2024-06-01-a",
    "transferOptions": {
      "items": [
        { "from": { "operator": true }, "to": { "externalReferenceId": "user-123" }, "amount": 10.00, "memo": "Reward" },
        { "from": { "externalReferenceId": "user-123" }, "to": { "operator": true }, "amount": 2.50 },
        { "from": { "externalReferenceId": "user-123" }, "to": { "externalReferenceId": "user-456" }, "amount": 5.00 }
      ]
    }
  }
}
```

## Variables — depositar saldo en efectivo

Fondea la cuenta de gasto de un usuario conectado desde un método de pago que **él posee** — exactamente uno de `bankCardId` o `bankAccountId` por elemento. Segmentación `SELECTED`, un elemento por objetivo. `userCashBalanceId` es opcional (por defecto la cuenta de gasto permitida/predeterminada del usuario).

```json theme={null}
{
  "input": {
    "operation": "DEPOSIT_CASH_BALANCE",
    "idempotencyKey": "deposits-2024-06-01-a",
    "targetSpec": { "mode": "SELECTED", "targets": ["user-123", "user-456"] },
    "depositOptions": {
      "items": [
        { "externalReferenceId": "user-123", "amount": 10.00, "bankCardId": "b1a2…", "memo": "Top-up" },
        { "externalReferenceId": "user-456", "amount": 25.00, "bankAccountId": "c3d4…" }
      ]
    }
  }
}
```

## Variables — actualizar metadatos de transacción

Edita `memo` y/o `transactionCategory` en las transacciones de un usuario. Segmentación `SELECTED`, un elemento por objetivo, **como máximo 100 ediciones en total** por envío. Un campo establecido en `null` lo borra; un campo omitido queda sin cambios. Las ediciones de metadatos se ejecutan **sincrónicamente** — el trabajo devuelto ya es terminal, por lo que puedes leer los resultados por edición de inmediato sin consultar.

```json theme={null}
{
  "input": {
    "operation": "UPDATE_TRANSACTION_METADATA",
    "idempotencyKey": "metadata-2024-06-01-a",
    "targetSpec": { "mode": "SELECTED", "targets": ["user-123"] },
    "metadataOptions": {
      "items": [
        {
          "externalReferenceId": "user-123",
          "edits": [
            { "recordId": "3f2a…", "memo": "Team lunch", "transactionCategory": "Meals" },
            { "recordId": "9b7c…", "memo": null }
          ]
        }
      ]
    }
  }
}
```

## Respuesta

```json theme={null}
{
  "data": {
    "submitBulkOperation": {
      "jobId": "9c1e6f2a-1d4b-4a2e-8f0c-2b7e5a9d1234",
      "status": "QUEUED",
      "operation": "CREATE_TRANSFER",
      "requestedTargetCount": 3,
      "acceptedItemCount": 3,
      "succeededItemCount": 0,
      "failedItemCount": 0,
      "skippedItemCount": 0,
      "createdAt": "2024-06-01T15:04:05Z"
    }
  }
}
```

## Argumentos

| Parámetro                     | Tipo                       | Descripción                                                                                                                                                                 |
| ----------------------------- | -------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `input.operation`             | `BulkOperationType!`       | Uno de `GET_BALANCES_EXPORT`, `GET_TRANSACTIONS_EXPORT`, `CREATE_TRANSFER`, `DEPOSIT_CASH_BALANCE`, `UPDATE_TRANSACTION_METADATA`.                                          |
| `input.idempotencyKey`        | `String`                   | Tu clave única para este envío. Obligatoria (mediante este campo o el encabezado `Idempotency-Key`).                                                                        |
| `input.targetSpec`            | `BulkTargetSpecInput`      | A cuáles usuarios conectados aplica la operación. Obligatorio para exportaciones, depósitos y metadatos; se ignora para `CREATE_TRANSFER` (los elementos se autodescriben). |
| `input.exportOptions`         | `BulkExportOptionsInput`   | Ventana `createdGte` / `createdLte` e `includeMetadata`. Solo operaciones de exportación.                                                                                   |
| `input.transferOptions.items` | `[BulkTransferItemInput!]` | Endpoints `from` / `to` por transferencia, `amount`, `memo` opcional. Solo `CREATE_TRANSFER`.                                                                               |
| `input.depositOptions.items`  | `[BulkDepositItemInput!]`  | `amount` por objetivo, uno de `bankCardId` / `bankAccountId`, `userCashBalanceId` / `memo` opcionales. Solo `DEPOSIT_CASH_BALANCE`.                                         |
| `input.metadataOptions.items` | `[BulkMetadataItemInput!]` | `edits` por objetivo (`recordId`, `memo?`, `transactionCategory?`). Solo `UPDATE_TRANSACTION_METADATA`; ≤100 ediciones en total.                                            |

## Campos de respuesta

| Campo                                    | Descripción                                                                                                 |
| ---------------------------------------- | ----------------------------------------------------------------------------------------------------------- |
| `jobId`                                  | El id del trabajo. Úsalo para [seguir estado y resultados](/track-bulk-job).                                |
| `status`                                 | Estado de ciclo de vida: `QUEUED`, `RUNNING`, `COMPLETED`, `COMPLETED_WITH_ERRORS`, `FAILED` o `CANCELLED`. |
| `operation`                              | La operación enviada.                                                                                       |
| `requestedTargetCount`                   | Objetivos abordados por la solicitud.                                                                       |
| `acceptedItemCount`                      | Elementos que se procesarán (uno por objetivo aceptado).                                                    |
| `succeededItemCount` / `failedItemCount` | Resultados por elemento; se completan a medida que se ejecuta el trabajo.                                   |
| `skippedItemCount`                       | Objetivos rechazados al enviar (por ejemplo, falta el alcance requerido).                                   |
| `createdAt`                              | Cuándo se creó el trabajo.                                                                                  |

<Note>
  Las fallas son **por elemento** — la falla de un objetivo nunca falla el trabajo (ver el [contrato de fallas por objetivo](/bulk-api#per-target-failure-contract)). La solicitud en sí solo se rechaza por una falla de autenticación, una operación inválida, un límite excedido o cuando *todos* los objetivos son irresolubles. Después de un estado terminal, lee el detalle por elemento — incluyendo el recurso que produjo cada escritura — con [Seguir un trabajo por lotes](/track-bulk-job).
</Note>

## Errores

Todo el envío se rechaza (no se crea trabajo) solo en estos casos. Todo lo demás se convierte en un resultado por elemento que lees vía [Seguir un trabajo por lotes](/track-bulk-job#per-item-failure-codes).

| Error                        | HTTP | Cuándo                                                                                                                                                                                                                                                                                                   |
| ---------------------------- | ---- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `BulkApiAccessDenied`        | 403  | Tu aplicación no tiene la capacidad de API por lotes.                                                                                                                                                                                                                                                    |
| `IdempotencyKeyConflict`     | 409  | Se enviaron tanto el encabezado `Idempotency-Key` como la entrada `idempotencyKey` y no coinciden.                                                                                                                                                                                                       |
| `IdempotencyKeyReused`       | 409  | Esta clave de idempotencia ya se usó para una solicitud **diferente**. (Un reenvío idéntico devuelve el trabajo original).                                                                                                                                                                               |
| `TargetLimitExceeded`        | 422  | La solicitud hace referencia a más de 10,000 usuarios distintos. Divide el lote y reenvía.                                                                                                                                                                                                               |
| `ArgumentsInvalid`           | 400  | Falló la validación del payload (por ejemplo, un endpoint de transferencia que no es exactamente uno de operador/usuario, `from` == `to`, un monto menor a un centavo, un memo demasiado largo, >100 ediciones de metadatos) **o** todos los objetivos eran irresolubles / faltaba el alcance requerido. |
| `AmbiguousSourceCashBalance` | 422  | *(`CREATE_TRANSFER` solamente)* Una transferencia con origen en el operador, pero tu ruteo de pagos nombra múltiples saldos de efectivo sin un predeterminado. Contacta a Fluz para configurar un saldo de pagos predeterminado.                                                                         |
