> ## 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.

# Anular un contacto

> Cierra un contacto para que no vuelva a llamarse en ese lote.

La anulación afecta únicamente al contacto dentro del lote indicado. Identifica el contacto en el body mediante uno de estos campos:

* `identifier`: identificador externo definido en el sistema de origen. Es la opción recomendada para integrar un CRM u otro sistema externo.
* `contactId`: identificador interno generado por Inagent.

Envía exactamente uno de los dos campos. Puedes añadir `reason` para dejar registrado el motivo de la anulación.

```json identifier theme={null}
{
  "identifier": "CLI-001",
  "reason": "El cliente ya regularizó su deuda"
}
```

Para excluir una dirección de toda la cuenta, utiliza el endpoint de bloqueo. Anular un contacto solo impide que vuelva a llamarse dentro de este lote; la dirección continúa disponible para otros lotes.


## OpenAPI

````yaml api-reference/dialer/openapi.yaml PUT /batches/{batchId}/contacts/cancel/
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/{batchId}/contacts/cancel/:
    parameters:
      - $ref: '#/components/parameters/BatchId'
    put:
      tags:
        - Contactos
      summary: Anula la marcación de un contacto
      description: >
        Cierra un contacto para que no vuelva a ser llamado en este lote. Se
        utiliza, por ejemplo, cuando

        el cliente ya ha regularizado su situación o solicita no recibir más
        llamadas de esta campaña.


        El contacto se puede identificar mediante su `identifier` externo o
        mediante el `contactId`

        interno. Debe enviarse exactamente uno de los dos campos. Para
        integraciones con un CRM u otro

        sistema de origen, se recomienda utilizar `identifier`.


        El contacto pasa a estado final con `result: CANCELLED_BY_USER`, valor
        que queda disponible en el

        detalle del contacto y en su historial de intentos.


        ## Anulación y bloqueo son operaciones distintas


        | Operación | Alcance | Cuándo utilizarla |

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

        | Anulación (esta operación) | Un contacto en un lote concreto | No se
        desea seguir llamando en esta campaña |

        | Bloqueo (`POST /blocked-addresses`) | Una dirección en toda la cuenta
        | La persona solicita no ser contactada nunca |


        La anulación mantiene la dirección disponible para otras campañas. Si la
        persona ha solicitado no

        ser contactada, es necesario bloquear la dirección.


        ## Comprobación previa


        Antes de aceptar la solicitud se comprueba el estado del contacto. Un
        identificador inexistente

        devuelve `404`, y un contacto ya cerrado devuelve `409`, en lugar de
        aceptar una anulación que no

        llegaría a aplicarse.


        Si esa comprobación no puede realizarse, la respuesta es `503` y la
        anulación no se registra. En

        ese caso conviene reintentar.


        ## Contactos ya cerrados


        Si el contacto ya está en estado `contacted` o `finished`, la respuesta
        es `409` con

        `code: CONTACT_TERMINAL_STATE`. Suele indicar que el estado del contacto
        en el sistema de origen

        no coincide con el real.


        ## Efecto en los recuentos del lote


        Un contacto anulado se cuenta como finalizado y no como contactado:
        incrementa `finishedCount` y

        no `contactedCount`, de modo que la tasa de contactación no se ve
        afectada y el lote puede

        completarse.


        ## Procesamiento


        La respuesta `200` confirma que la solicitud se ha aceptado. El cierre
        lo aplica el motor de

        marcación, de modo que una consulta inmediatamente posterior puede no
        reflejarlo todavía.
      operationId: cancelContact
      parameters:
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              oneOf:
                - required:
                    - identifier
                  not:
                    required:
                      - contactId
                - required:
                    - contactId
                  not:
                    required:
                      - identifier
              properties:
                identifier:
                  type: string
                  description: Identificador del contacto en el sistema de origen.
                  example: CLI-001
                contactId:
                  type: string
                  format: uuid
                  description: >-
                    Identificador interno asignado por Inagent al importar el
                    contacto.
                  example: 7d2f8a1b-3c4e-4f50-9a6b-2c3d4e5f6a7b
                reason:
                  type: string
                  description: >
                    Motivo de la anulación. Queda registrado en la dirección que
                    iba a marcarse y aparece

                    en el detalle del contacto. Es informativo.
                  example: El cliente ya regularizó su deuda
            examples:
              porIdentifier:
                summary: Anular mediante el identificador externo
                value:
                  identifier: CLI-001
                  reason: El cliente ya regularizó su deuda
              porContactId:
                summary: Anular mediante el identificador interno
                value:
                  contactId: 7d2f8a1b-3c4e-4f50-9a6b-2c3d4e5f6a7b
                  reason: El cliente ya regularizó su deuda
      responses:
        '200':
          description: Anulación aceptada.
          headers:
            Idempotency-Replayed:
              $ref: '#/components/headers/IdempotencyReplayed'
            Idempotency-Status:
              $ref: '#/components/headers/IdempotencyStatus'
          content:
            application/json:
              schema:
                type: object
                required:
                  - contactId
                  - batchId
                  - cancelled
                  - status
                  - result
                properties:
                  contactId:
                    type: string
                    format: uuid
                  batchId:
                    type: string
                    format: uuid
                  cancelled:
                    type: boolean
                  status:
                    type: string
                    enum:
                      - finished
                    description: >
                      Estado en el que queda el contacto. `finished` es un
                      estado final y excluye al

                      contacto de la marcación.
                  result:
                    type: string
                    enum:
                      - CANCELLED_BY_USER
                    description: >
                      Último resultado registrado del contacto. Permite
                      distinguir una anulación de un

                      cierre por intentos agotados.
                  reason:
                    type: string
              example:
                contactId: 7d2f8a1b-3c4e-4f50-9a6b-2c3d4e5f6a7b
                batchId: 3f8a1b2c-5d6e-4f70-8a9b-1c2d3e4f5a6b
                cancelled: true
                status: finished
                result: CANCELLED_BY_USER
                reason: El cliente ya regularizó su deuda
        '400':
          description: >
            Falta el identificador del contacto, se enviaron `identifier` y
            `contactId` a la vez, o la

            `Idempotency-Key` tiene un formato incorrecto.
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: '#/components/schemas/SimpleError'
                  - $ref: '#/components/schemas/IdempotencyKeyError'
              examples:
                claveInvalida:
                  summary: '`Idempotency-Key` mal formada'
                  value:
                    code: INVALID_IDEMPOTENCY_KEY
                    message: 'Invalid Idempotency-Key header: it must not be empty'
                    details:
                      - field: Idempotency-Key
                        code: EMPTY
                        reason: must not be empty
                    meta:
                      rejection: EMPTY
        '401':
          description: Credencial ausente o inválida.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SimpleError'
              example:
                message: Unauthorized
        '404':
          description: >
            El lote no existe o no pertenece a la cuenta, o el contacto no
            pertenece a ese lote.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SimpleError'
              examples:
                loteInexistente:
                  value:
                    code: BATCH_NOT_FOUND
                    message: Batch not found
                contactoInexistente:
                  value:
                    code: CONTACT_NOT_FOUND
                    message: Contact not found in this batch
        '409':
          description: |
            El contacto ya está cerrado, o hay un conflicto de idempotencia.
          headers:
            Retry-After:
              description: >
                Segundos a esperar antes de reintentar. Solo con
                `IDEMPOTENCY_KEY_IN_FLIGHT`.
              schema:
                type: integer
              example: 2
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: '#/components/schemas/ValidationError'
                  - $ref: '#/components/schemas/IdempotencyConflictError'
              examples:
                yaCerrado:
                  summary: El contacto ya está en estado terminal
                  value:
                    code: CONTACT_TERMINAL_STATE
                    message: Contact is already closed and cannot be cancelled
                    details:
                      - field: contactId
                        code: CONTACT_TERMINAL_STATE
                        reason: contact is in terminal state 'contacted'
                        meta:
                          status: contacted
                    meta:
                      status: contacted
                enVuelo:
                  value:
                    code: IDEMPOTENCY_KEY_IN_FLIGHT
                    message: >-
                      A request with this Idempotency-Key is still being
                      processed. Retry shortly.
                    meta:
                      retryAfterSeconds: 2
        '500':
          $ref: '#/components/responses/InternalError'
        '503':
          description: >
            No se ha podido comprobar el estado del contacto, por lo que la
            anulación no se ha

            registrado.


            No se ha aplicado ningún cambio. Reintente la solicitud.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SimpleError'
              example:
                code: TEMPORARILY_UNAVAILABLE
                message: >-
                  Contact data is temporarily unavailable, so the cancellation
                  could not be verified
components:
  parameters:
    BatchId:
      name: batchId
      in: path
      required: true
      schema:
        type: string
        format: uuid
      description: Id del lote.
    IdempotencyKey:
      name: Idempotency-Key
      in: header
      required: false
      schema:
        type: string
        maxLength: 255
        pattern: ^[\x20-\x7E]+$
      description: >
        Clave para hacer el reintento seguro. Opcional.


        Un UUID por operación lógica, reusado en los reintentos de ese mismo
        envío. Ver "Reintentos

        seguros" en la descripción de la API.
      example: 3f8a1b2c-5d6e-4f70-8a9b-1c2d3e4f5a6b
  headers:
    IdempotencyReplayed:
      description: >
        Presente con valor `true` cuando la respuesta es una repetición de un
        request ya procesado con

        la misma clave. No se ejecutó nada nuevo.
      schema:
        type: string
        enum:
          - 'true'
    IdempotencyStatus:
      description: >
        Aparece con valor `bypassed` cuando se envió una clave válida pero la
        petición se procesó sin

        protección de idempotencia, porque el servicio que la gestiona no estaba
        disponible.


        La respuesta es válida, pero esa petición concreta no cuenta con la
        garantía de reintento seguro:

        si no se recibe respuesta, verifique el estado antes de reintentar.
      schema:
        type: string
        enum:
          - bypassed
  schemas:
    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
    IdempotencyKeyError:
      type: object
      required:
        - code
        - message
      description: >
        La clave de idempotencia recibida no cumple los requisitos de formato.


        Se rechaza en lugar de ignorarse, para no procesar la petición sin la
        protección que el cliente

        espera tener.
      properties:
        code:
          type: string
          enum:
            - INVALID_IDEMPOTENCY_KEY
        message:
          type: string
        details:
          type: array
          items:
            type: object
            properties:
              field:
                type: string
                enum:
                  - Idempotency-Key
              code:
                type: string
                enum:
                  - EMPTY
                  - TOO_LONG
                  - INVALID_CHARACTERS
              reason:
                type: string
              meta:
                type: object
                additionalProperties: true
        meta:
          type: object
          description: >
            `rejection` es el dato estable para ramificar; el texto de `reason`
            es informativo.
          properties:
            rejection:
              type: string
              enum:
                - EMPTY
                - TOO_LONG
                - INVALID_CHARACTERS
          additionalProperties: true
    ValidationError:
      type: object
      required:
        - message
      properties:
        code:
          type: string
          enum:
            - VALIDATION_FAILED
          description: >
            Presente cuando el rechazo corresponde a la validación de contactos.
            No aparece en los

            rechazos por límites de la petición, como un cuerpo vacío o más de
            1000 contactos.
        message:
          type: string
        details:
          description: >
            Detalle del rechazo. La estructura varía según el caso: una lista de
            textos para los límites

            de la petición, o una lista de `FieldRejection` cuando el rechazo es
            de validación de

            contactos.
          oneOf:
            - type: array
              items:
                type: string
            - type: array
              items:
                $ref: '#/components/schemas/FieldRejection'
    IdempotencyConflictError:
      type: object
      required:
        - code
        - message
      description: >
        Conflicto de idempotencia. Los dos casos se resuelven distinto:


        * `IDEMPOTENCY_KEY_IN_FLIGHT`: hay una petición idéntica en proceso.
        Espere el tiempo indicado en
          `Retry-After` y reintente con la misma clave.
        * `IDEMPOTENCY_KEY_REUSED`: la clave ya se utilizó con un cuerpo
        distinto. Utilice una clave nueva
          para la nueva petición.
      properties:
        code:
          type: string
          enum:
            - IDEMPOTENCY_KEY_IN_FLIGHT
            - IDEMPOTENCY_KEY_REUSED
        message:
          type: string
        meta:
          type: object
          additionalProperties: true
          properties:
            retryAfterSeconds:
              type: integer
    FieldRejection:
      type: object
      required:
        - index
        - field
        - code
        - reason
      description: >
        Campo rechazado de un contacto concreto.


        Se incluyen todos los rechazos, no solo el primero de cada contacto: un
        contacto con tres campos

        incorrectos genera tres entradas.


        Utilice `code`, que es estable, para tratar el error de forma
        programática. El campo `reason` es

        un texto descriptivo de apoyo y puede cambiar sin aviso.
      properties:
        index:
          type: integer
          description: Posición del contacto en el array enviado, base 0.
          example: 3
        field:
          type: string
          description: >
            Ruta del campo rechazado, precedida por la posición del contacto.
            Permite localizarlo en la

            petición enviada.
          example: contacts[3].addresses[0].address
        code:
          type: string
          enum:
            - REQUIRED
            - INVALID_TYPE
            - INVALID_PHONE_FORMAT
            - INVALID_EMAIL_FORMAT
            - INVALID_ADDRESS_TYPE
            - INVALID_PRIORITY
            - EMPTY_ARRAY
            - INVALID_DATE
            - DATE_NOT_FUTURE
            - DATE_OUT_OF_RANGE
            - DUPLICATED_IN_REQUEST
          description: >
            Código estable del motivo del rechazo. Es el valor recomendado para
            tratar el error de forma

            programática y para traducir el mensaje al idioma del usuario final.
        reason:
          type: string
          description: >
            Descripción del rechazo, en español. Informativa: puede cambiar sin
            aviso.
        meta:
          type: object
          additionalProperties: true
          description: >
            Datos del rechazo: el valor recibido, el esperado o el límite
            aplicado. Permiten construir un

            mensaje propio sin necesidad de interpretar el texto de `reason`.
          example:
            received: '123'
            expected: digits only, 7-15 characters
        error:
          type: string
          deprecated: true
          description: >
            Contiene el mismo valor que `reason`. Se mantiene por compatibilidad
            con versiones anteriores;

            utilice `reason`.
  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.

````