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

# Consultar una operación

> Consulta el estado y el resultado de una operación asíncrona.

Utiliza el `operationId` devuelto por una operación con respuesta `202` para seguir su progreso y recuperar su resultado.

Revisa primero el campo `kind`: `contacts.load` devuelve el desglose de los contactos cargados, mientras que `batch.lifecycle` informa del progreso y la etapa de una operación sobre el lote.

En una carga de contactos, `recontactedCount` indica cuántos contactos existentes se volvieron a poner en marcación mediante `onDuplicate: "recontact"`.


## OpenAPI

````yaml api-reference/dialer/openapi.yaml GET /batches/operations/{operationId}
openapi: 3.1.0
info:
  title: AutoContact API
  version: final
  summary: Gestión de lotes de marcación automática y carga de contactos.
  description: >
    API de AutoContact: gestión de lotes de marcación automática y de los
    contactos que los integran.


    Todos los recursos se publican bajo `/autocontact/api/v1/`.


    ## Autenticación


    Las peticiones se autentican con el header `apikey`. La misma credencial
    permite operar los lotes

    de la cuenta a los que tenga acceso. El servicio deriva la cuenta de esa
    credencial y verifica que

    el recurso solicitado le pertenezca.


    Un lote que no pertenece a la cuenta responde `404`, igual que un lote
    inexistente.


    ## Operaciones asíncronas


    Algunas operaciones responden `202` en lugar de `200`: la petición se acepta
    y el trabajo continúa

    en segundo plano. Es el caso de la carga de contactos, el arranque y la
    eliminación de un lote.


    Esas respuestas incluyen un `operationId` que se consulta en

    `GET /batches/operations/{operationId}` para conocer el resultado.


    `POST /batches` no genera una operación: devuelve directamente el `batchId`,
    el estado `created` y

    el origen del lote, sin `operationId`.


    Tiempo de retención de las operaciones:


    | Tipo de operación | Disponible durante |

    |---|---|

    | Carga de contactos | 6 horas |

    | Ciclo de vida del lote (arranque, archivado, eliminación) | 1 hora, y 10
    minutos desde que finaliza |


    ## Reintentos seguros con `Idempotency-Key`


    Las operaciones de escritura aceptan el header opcional `Idempotency-Key`.
    Resuelve el caso en que

    se produce un error de red después de que la petición llegó al servicio: sin
    este header no es

    posible determinar si el trabajo se procesó, y reintentar podría duplicar
    llamadas telefónicas.


    Con el header, un reintento que use la misma clave y el mismo cuerpo
    devuelve la respuesta original

    sin procesar nada de nuevo. Las claves se conservan 24 horas.


    Recomendación de uso: generar un identificador único (por ejemplo un UUID)
    antes del primer intento

    y reutilizarlo solo en los reintentos de esa misma operación.


    Requisitos de la clave: entre 1 y 255 caracteres, solo ASCII imprimible. Una
    clave que no cumpla

    estos requisitos se rechaza con `400 INVALID_IDEMPOTENCY_KEY` en lugar de
    ignorarse, para evitar

    procesar la petición sin la protección que el cliente espera tener.


    ### Headers de respuesta


    | Header | Cuándo aparece | Significado |

    |---|---|---|

    | `Idempotency-Replayed: true` | En un reintento | La respuesta corresponde
    a la petición original; no se procesó de nuevo |

    | `Retry-After` | Junto a `409 IDEMPOTENCY_KEY_IN_FLIGHT` | Segundos a
    esperar antes de reintentar |

    | `Idempotency-Status: bypassed` | Excepcionalmente | La petición se procesó
    **sin** protección de idempotencia |


    El header `Idempotency-Status: bypassed` indica que el servicio de
    idempotencia no estaba

    disponible y la petición se procesó igualmente, para no interrumpir la
    operación. La respuesta es

    válida, pero esa petición concreta no quedó protegida: si no se recibe
    respuesta, se recomienda

    verificar el estado antes de reintentar.


    Las respuestas con error `5xx` no se almacenan, de modo que un reintento con
    la misma clave vuelve a

    procesar la petición.
servers:
  - url: https://api.backend.inconcertcc.com/autocontact/api/v1
    description: API de Inagent
security:
  - ApiKeyAuth: []
tags:
  - name: Lotes
    description: |
      Creación, consulta y control de lotes de marcación.
  - name: Contactos
    description: |
      Carga de contactos en un lote, reagendamiento y anulación.
  - name: Direcciones bloqueadas
    description: |
      Direcciones excluidas de la marcación en toda la cuenta.
  - name: Operaciones
    description: |
      Seguimiento de las operaciones asíncronas que devuelven `202`.
paths:
  /batches/operations/{operationId}:
    parameters:
      - name: operationId
        in: path
        required: true
        schema:
          type: string
          format: uuid
        description: >
          Identificador de la operación, devuelto por la operación asíncrona que
          se desea consultar.
    get:
      tags:
        - Operaciones
      summary: Consulta el estado de una operación
      description: >
        Devuelve el estado y el resultado de una operación asíncrona.


        ## Tipos de operación


        Esta operación resuelve cualquier `operationId` devuelto por la API. El
        campo `kind` indica de

        qué tipo se trata, ya que la información que aporta cada uno es
        distinta:


        | `kind` | Origen | Información que aporta |

        |---|---|---|

        | `contacts.load` | Carga de contactos | Recuento de contactos
        insertados, actualizados, duplicados e inválidos |

        | `batch.lifecycle` | Creación, arranque, archivado, eliminación y
        exportación de un lote | Porcentaje de avance y etapa en curso |


        Utilice `kind` para determinar la estructura de la respuesta. Una carga
        de contactos no incluye

        `progress`, y una operación de ciclo de vida no incluye `insertedCount`.


        ## Estados de una carga de contactos


        | `status` | Significado |

        |---|---|

        | `pending` | Aceptada, sin bloques procesados todavía |

        | `running` | En proceso |

        | `succeeded` | Finalizada; se insertaron todos los contactos |

        | `partial` | Finalizada; se insertó una parte |

        | `failed` | Finalizada; no se insertó ninguno |


        El estado `partial` no indica un error: en una carga masiva es un
        resultado habitual, ya que

        algunos contactos pueden descartarse por duplicado o por validación.


        ## Interpretación de los recuentos


        `requestedCount` es el número de contactos enviados. Los campos
        siguientes detallan qué ocurrió

        durante el procesamiento:


        * `insertedCount`: contactos añadidos como nuevos

        * `updatedCount`: contactos que ya existían y se actualizaron

        * `duplicateCount`: contactos que ya existían y se descartaron

        * `invalidCount`: contactos rechazados por validación

        * `recontactedCount`: contactos existentes que volvieron a ponerse en
        marcación


        Mientras la operación está en proceso, los contadores todavía pueden
        estar incompletos.


        ## Campo `errors`


        Contiene una muestra de hasta 20 rechazos. Los recuentos son el dato
        exacto; esta lista permite

        identificar el motivo de los rechazos.


        ## Tiempo de retención


        | `kind` | Disponible durante |

        |---|---|

        | `contacts.load` | 6 horas |

        | `batch.lifecycle` | 1 hora, y 10 minutos desde que finaliza |


        Una operación de ciclo de vida consultada más de 10 minutos después de
        finalizar devuelve `404`,

        aunque se haya completado correctamente. Para conservar el resultado,
        consúltelo al finalizar, o

        bien consulte el estado del lote, que es permanente.
      operationId: getOperation
      responses:
        '200':
          description: Estado de la operación.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Operation'
              examples:
                terminadaParcial:
                  summary: Terminó, entró una parte
                  value:
                    kind: contacts.load
                    operationId: 9f1c7e2a-4b3d-4a1e-9c8f-2d5b6e7a8c90
                    batchId: 3f8a1b2c-5d6e-4f70-8a9b-1c2d3e4f5a6b
                    type: contacts.insert
                    status: partial
                    requestedCount: 150
                    totalChunks: 3
                    chunksCompleted: 3
                    insertedCount: 140
                    invalidCount: 4
                    duplicateCount: 6
                    updatedCount: 0
                    recontactedCount: 0
                    errors:
                      - identifier: CLI-042
                        reason: >-
                          addresses[0]: formato inválido para tipo 'phone':
                          '123' (debe contener solo dígitos, 7-15 caracteres)
                      - identifier: CLI-077
                        reason: >-
                          Duplicate of an existing contact, rejected by the
                          batch policy (matched phone)
                    createdAt: '2026-08-10T12:00:00.000Z'
                    completedAt: '2026-08-10T12:00:04.120Z'
                enCurso:
                  summary: Todavía procesándose
                  value:
                    kind: contacts.load
                    operationId: 9f1c7e2a-4b3d-4a1e-9c8f-2d5b6e7a8c90
                    batchId: 3f8a1b2c-5d6e-4f70-8a9b-1c2d3e4f5a6b
                    type: contacts.insert
                    status: running
                    requestedCount: 150
                    totalChunks: 3
                    chunksCompleted: 1
                    insertedCount: 50
                    invalidCount: 0
                    duplicateCount: 0
                    updatedCount: 0
                    recontactedCount: 0
                    errors: []
                    createdAt: '2026-08-10T12:00:00.000Z'
                    completedAt: null
                arranqueEnCurso:
                  summary: Arranque de lote, a mitad de la primera etapa
                  value:
                    kind: batch.lifecycle
                    operationId: 5c2e8a14-7b09-4f3d-a1e6-8d4b2c9f0e71
                    batchId: 3f8a1b2c-5d6e-4f70-8a9b-1c2d3e4f5a6b
                    type: start
                    status: processing
                    progress: 48
                    processedCount: 242000
                    totalCount: 250000
                    phase: export
                    phaseNumber: 1
                    phaseCount: 2
                    phaseLabel: Exportando contactos
                    errorMessage: null
                    createdAt: '2026-08-10T12:00:00.000Z'
                    completedAt: null
                altaTerminada:
                  summary: Alta de lote terminada
                  value:
                    kind: batch.lifecycle
                    operationId: 7a3f1d90-2c5b-4e8a-b6f4-9d1e0c3a5b82
                    batchId: 3f8a1b2c-5d6e-4f70-8a9b-1c2d3e4f5a6b
                    type: create
                    status: completed
                    progress: 100
                    processedCount: 5000
                    totalCount: 5000
                    phase: null
                    phaseNumber: null
                    phaseCount: null
                    phaseLabel: null
                    errorMessage: null
                    createdAt: '2026-08-10T11:58:00.000Z'
                    completedAt: '2026-08-10T11:59:12.000Z'
        '400':
          description: Falta el identificador de la operación.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SimpleError'
              example:
                code: MISSING_PARAMETER
                message: 'Missing required parameter: operationId'
        '404':
          description: >
            La operación no existe, ha caducado o no pertenece a la cuenta.


            Los plazos de retención se detallan en la descripción de esta
            operación y varían según el

            tipo de operación.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SimpleError'
              example:
                code: OPERATION_NOT_FOUND
                message: Operation not found or expired
        '500':
          $ref: '#/components/responses/InternalError'
components:
  schemas:
    Operation:
      oneOf:
        - $ref: '#/components/schemas/ContactsLoadOperation'
        - $ref: '#/components/schemas/BatchLifecycleOperation'
      discriminator:
        propertyName: kind
        mapping:
          contacts.load:
            $ref: '#/components/schemas/ContactsLoadOperation'
          batch.lifecycle:
            $ref: '#/components/schemas/BatchLifecycleOperation'
      description: >
        Una operación asincrónica. Las dos variantes se distinguen por `kind`:
        no son el mismo recurso

        con campos opcionales, sino dos formas distintas que informan cosas
        distintas.
    SimpleError:
      type: object
      required:
        - message
      description: >
        Error con mensaje descriptivo.


        Algunos traen `code` estable (los más nuevos); otros solo `message`.


        Los mensajes pueden aparecer en español o en inglés según el caso, y
        algunos errores todavía no

        incluyen `code`. Cuando esté presente, utilice `code` para tratar el
        error de forma programática.
      properties:
        code:
          type: string
          description: >
            Presente en los errores ya normalizados (`MISSING_PARAMETER`,
            `OPERATION_NOT_FOUND`,

            `VALIDATION_FAILED`, `INVALID_IDEMPOTENCY_KEY`, y los de
            idempotencia).
        message:
          type: string
    ContactsLoadOperation:
      type: object
      description: |
        Una carga de contactos. Informa qué pasó con cada contacto del request.
      required:
        - kind
        - operationId
        - batchId
        - type
        - status
        - requestedCount
        - totalChunks
        - chunksCompleted
        - insertedCount
        - invalidCount
        - duplicateCount
        - updatedCount
        - recontactedCount
        - errors
        - createdAt
      properties:
        kind:
          type: string
          enum:
            - contacts.load
          description: >
            Identifica el tipo de operación. Utilice este campo para determinar
            la estructura de la respuesta.
        operationId:
          type: string
          format: uuid
        batchId:
          type: string
          format: uuid
          description: >
            El lote sobre el que corrió la carga. Viaja en la respuesta para que
            no tengas que

            recordarlo.
        type:
          type: string
          enum:
            - contacts.insert
        status:
          type: string
          enum:
            - pending
            - running
            - succeeded
            - partial
            - failed
          description: >
            `partial` significa que entró una parte de los contactos. En una
            carga masiva es un

            desenlace normal, no un error.
        requestedCount:
          type: integer
          description: Número de contactos enviados en la petición.
          example: 150
        totalChunks:
          type: integer
          description: >
            Bloques en los que se partió la carga. La operación cierra cuando
            todos reportaron.
          example: 3
        chunksCompleted:
          type: integer
          description: >
            Bloques ya procesados. Con `chunksCompleted < totalChunks` los
            contadores todavía están

            incompletos.
          example: 3
        insertedCount:
          type: integer
          description: Contactos que entraron como nuevos.
        invalidCount:
          type: integer
          description: Rechazados por validación de datos.
        duplicateCount:
          type: integer
          description: Descartados por la política de duplicados del lote.
        updatedCount:
          type: integer
          description: >
            Ya existían y se les aplicó lo enviado. Cubre tanto el merge como el
            reemplazo: la

            diferencia entre los dos la decide la política del lote, no el
            resultado de la carga.
        recontactedCount:
          type: integer
          description: |
            Contactos existentes que volvieron a ponerse en marcación mediante
            `onDuplicate: recontact`.
        errors:
          type: array
          maxItems: 20
          description: >
            Muestra de rechazos, hasta 20. Los contadores son el dato preciso;
            esto sirve para

            entender por qué se rechazaron.
          items:
            type: object
            required:
              - reason
            properties:
              identifier:
                type: string
                description: >
                  Identificador del contacto rechazado, cuando puede
                  determinarse.
                example: CLI-042
              reason:
                type: string
        createdAt:
          type: string
          format: date-time
        completedAt:
          type:
            - string
            - 'null'
          format: date-time
          description: >
            Cuándo terminó de procesarse. `null` mientras `status` sea `pending`
            o `running`.
    BatchLifecycleOperation:
      type: object
      description: >
        Una operación de ciclo de vida del lote: el alta, el arranque, el
        archivado, el borrado o una

        exportación. Informa avance porcentual, no contadores por contacto.


        **Se retiene 1 hora, y solo 10 minutos desde que termina.** Es bastante
        menos que las 6 horas

        de una carga de contactos: si te importa el desenlace, consultalo cuando
        termina.
      required:
        - kind
        - operationId
        - batchId
        - type
        - status
        - progress
        - processedCount
        - totalCount
        - createdAt
      properties:
        kind:
          type: string
          enum:
            - batch.lifecycle
          description: >
            Identifica el tipo de operación. Utilice este campo para determinar
            la estructura de la respuesta.
        operationId:
          type: string
          format: uuid
        batchId:
          type: string
          format: uuid
        type:
          type: string
          enum:
            - create
            - start
            - delete
            - export
            - archive
        status:
          type: string
          enum:
            - processing
            - completed
            - error
          description: >
            Los valores son distintos de los de una carga de contactos. En una
            operación de ciclo de vida

            no existe el estado `partial`, ya que no procesa elementos que
            puedan rechazarse

            individualmente.
        progress:
          type: integer
          minimum: 0
          maximum: 100
          description: >
            Avance de la operación **completa**, con todas sus etapas.


            En una operación de varias etapas no se deriva de
            `processedCount`/`totalCount`: el

            arranque reparte el avance en mitades (exportación 0-50%, carga en
            el motor 50-100%), así

            que a mitad de la exportación es 25% aunque los contadores digan
            125.000 de 250.000. Los

            dos números responden preguntas distintas y se leen juntos con
            `phaseLabel`.
          example: 48
        processedCount:
          type: integer
          description: >
            Ítems procesados **en la etapa actual**. Es una cantidad real de
            dominio: en el arranque de

            un lote de 250.000 contactos, llega a 250.000 en cada una de las dos
            etapas.
        totalCount:
          type: integer
          description: Ítems totales de la etapa actual.
        phase:
          type:
            - string
            - 'null'
          enum:
            - export
            - load
            - null
          description: >
            Identificador estable de la etapa. `null` en operaciones de una sola
            etapa.
        phaseNumber:
          type:
            - integer
            - 'null'
          description: >-
            Índice de la etapa, empezando en 1. `null` si la operación tiene una
            sola.
        phaseCount:
          type:
            - integer
            - 'null'
          description: Cuántas etapas tiene la operación en total.
        phaseLabel:
          type:
            - string
            - 'null'
          description: >
            Texto de la etapa, para mostrar. Con `phaseNumber` y `phaseCount`
            arma un mensaje que se

            entiende sin explicar por qué el porcentaje no coincide con los
            contadores:

            "Paso 1 de 2 · Exportando contactos".
          example: Exportando contactos
        errorMessage:
          type:
            - string
            - 'null'
          description: |
            Motivo del fallo. Presente solo con `status: error`.
        createdAt:
          type: string
          format: date-time
        completedAt:
          type:
            - string
            - 'null'
          format: date-time
          description: |
            Cuándo terminó. `null` mientras `status` sea `processing`.
  responses:
    InternalError:
      description: Error interno.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/SimpleError'
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: apikey
      description: API key obtenida desde la interfaz de Inagent, dentro del marcador.

````