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

# Agente de IA: Reporte de pólizas y recibos pendientes en GNP

> Exporta el reporte completo de pólizas pendientes de GNP y lo entrega con los datos ya limpios, más el XLSX original.

El paso `gnp.pending-receipts` inicia sesión en el portal de intermediarios de
GNP, abre **Pólizas pendientes**, exporta el reporte y publica dos archivos:
`dataset.json` con los datos ya limpios, y `ExportResult.xlsx` con el original.
El output del paso trae el resumen y la referencia a ambos.

Limpia pero no filtra: devuelve todas las pólizas pendientes de todos los ramos
y conductos. Decidir cuáles te interesan es trabajo del siguiente paso.

## Requisito previo

Registra una credencial de tipo `gnp.portal` para el mismo tenant de la
automatización:

| Campo de la credencial | Contenido                        |
| ---------------------- | -------------------------------- |
| `username`             | Usuario o correo del portal GNP. |
| `password`             | Contraseña del portal GNP.       |

La definición del paso guarda únicamente el `credentialId`. La API no devuelve
ni guarda la contraseña dentro de la automatización.

## Campos del paso

| Campo          | Requerido | Descripción                                         |
| -------------- | --------- | --------------------------------------------------- |
| `actionKey`    | Sí        | Debe ser `gnp.pending-receipts`.                    |
| `credentialId` | Sí        | ID de una credencial `gnp.portal` del mismo tenant. |
| `name`         | No        | Nombre legible del paso.                            |

## Output

El output es pequeño y no crece con el número de pólizas: trae el resumen y la
referencia de los dos archivos. Puedes usar `{{steps.0.output.summary.receipts}}`
en un paso de HTTP sin escribir código.

```json theme={null}
{
  "schemaVersion": "insurance.policy-receipt-dataset.v1",
  "generatedAt": "2026-09-25T04:56:15.843Z",
  "summary": { "policies": 54, "receipts": 58, "documents": 0, "failed": 0 },
  "issues": [],
  "dataset": {
    "id": "<file-id>",
    "name": "dataset.json",
    "type": "application/json",
    "size": 123831,
    "sha256": "<sha256>",
    "sourceStepIndex": 0
  },
  "report": {
    "id": "<file-id>",
    "name": "ExportResult.xlsx",
    "type": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
    "size": 11310,
    "sha256": "<sha256>",
    "sourceStepIndex": 0
  }
}
```

<Note>
  Los datos van en un archivo y no dentro del output porque el output de un paso
  tiene un techo de 32 KB: arriba de eso la plataforma lo mueve a almacenamiento
  y el paso siguiente lo recibe vacío. El dataset pesa unos 2 KB por recibo, así
  que un reporte de 58 recibos ya lo cruzaría.
</Note>

## Qué trae `dataset.json`

El contrato es `insurance.policy-receipt-dataset.v1`, neutral entre aseguradoras.
Este extracto muestra los campos principales; el archivo incluye además
`rawFields`, `extensions` y `sources` en cada nivel.

```json theme={null}
{
  "schemaVersion": "insurance.policy-receipt-dataset.v1",
  "generatedAt": "2026-09-25T04:56:15.843Z",
  "policies": [
    {
      "insurer": { "code": "gnp", "name": "GNP Seguros" },
      "policyNumber": "00000623588738",
      "lineOfBusiness": { "code": "GMM", "name": "GMM", "raw": "GMM" },
      "parties": [{ "role": "policyholder", "name": "NOMBRE APELLIDO" }],
      "billing": {
        "firstCollectionChannel": {
          "value": "intermediary",
          "raw": "Intermediario"
        }
      },
      "receipts": [
        {
          "receiptNumber": "000000000001",
          "status": {
            "value": "pending",
            "raw": "Pendiente exigible Pendiente"
          },
          "period": { "startsAt": "2026-08-07", "endsAt": "2027-08-07" },
          "dueAt": "2027-08-07",
          "amounts": {
            "total": {
              "amount": 42686.52,
              "currency": "MXN",
              "raw": "$42,686.52 MXN"
            }
          },
          "documents": []
        }
      ]
    }
  ],
  "summary": { "policies": 1, "receipts": 1, "documents": 0, "failed": 0 },
  "issues": []
}
```

No necesitas convertir nada: los montos son números, las fechas están en formato
ISO, el conducto viene resuelto en `billing.firstCollectionChannel.value` y los
recibos vienen agrupados por póliza. Cada valor conserva su texto original en
`raw`.

Los números de póliza y de recibo son cadenas, porque GNP los reporta con ceros
a la izquierda. `lineOfBusiness` no es un catálogo cerrado.

## Ejemplo

```json theme={null}
{
  "name": "Cobranza pendiente GNP",
  "trigger": {
    "type": "cron",
    "cron": "0 8 * * *",
    "timezone": "America/Mexico_City"
  },
  "steps": [
    {
      "name": "Reporte de pólizas pendientes en GNP",
      "actionKey": "gnp.pending-receipts",
      "credentialId": "cred_0123456789ab"
    },
    {
      "name": "Decidir qué hacer con el reporte",
      "actionKey": "code.javascript",
      "code": "const archivo = files.fromStep(0).find((f) => f.name === 'dataset.json');\nconst { policies } = await archivo.json();\noutput = { polizas: policies.length };"
    }
  ]
}
```

Envía este objeto al endpoint de [Crear automatización](/automatizaciones/crear-automatizacion).
Después [publica](/automatizaciones/publicar-automatizacion) y
[activa](/automatizaciones/activar-automatizacion) la automatización.

## El XLSX original

Para programar contra los datos usa `dataset.json`, no este archivo: cada recibo
lleva en `rawFields` todas las columnas de su fila tal como las reportó GNP,
incluidas las que el contrato no modela. El XLSX está para auditar la corrida
contra el archivo exacto que produjo el portal, y para reenviarlo a alguien que
quiera una hoja de cálculo.

Se llama `ExportResult.xlsx`, tiene una hoja, y una fila por recibo:

| Columna                | Ejemplo                        |
| ---------------------- | ------------------------------ |
| `No. de Póliza`        | `000000123456789`              |
| `Ramo`                 | `Vida`                         |
| `Nombre de cliente`    | `NOMBRE APELLIDO APELLIDO`     |
| `No. de recibo/ Folio` | `000000000001`                 |
| `Estatus del recibo`   | `Pendiente exigible Pendiente` |
| `Monto`                | `$9,781.37 MXN`                |
| `Periodo desde`        | `01 - 01 - 2026`               |
| `Periodo hasta`        | `01 - 02 - 2026`               |
| `Conducto`             | `Intermediario`                |
| `Tipo de recibo`       | `Prima`                        |

Si de todos modos lo quieres leer, usa `file.xlsx()`:
[Archivos entre pasos](/automatizaciones/archivos-entre-pasos).

## Decidir sobre el reporte

Este ejemplo responde si cinco pólizas concretas están pendientes con conducto
`Intermediario`, y dice por qué no cuando no lo están.

```javascript theme={null}
const buscadas = ["621971555", "683620934", "683894612", "623588738", "378312037"];

const archivo = files.fromStep(0).find((file) => file.name === "dataset.json");
if (!archivo) {
  throw new Error("El paso anterior no publicó el dataset");
}
const { policies, summary } = await archivo.json();

// El número de póliza viene con ceros al frente, tal como lo reporta GNP.
const canonico = (valor) => String(valor).replace(/\D/g, "").replace(/^0+/, "");
const porPoliza = new Map(
  policies.map((policy) => [canonico(policy.policyNumber), policy]),
);

const pendientes = [];
const noEncontradas = [];

for (const numero of buscadas) {
  const policy = porPoliza.get(canonico(numero));
  const canal = policy?.billing?.firstCollectionChannel;

  if (policy && canal?.value === "intermediary") {
    pendientes.push({
      policyNumber: policy.policyNumber,
      ramo: policy.lineOfBusiness.name,
      recibos: policy.receipts.length,
      total: policy.receipts.reduce(
        (suma, receipt) => suma + (receipt.amounts.total?.amount ?? 0),
        0,
      ),
      vence: policy.receipts[0].dueAt,
    });
  } else {
    noEncontradas.push({
      policyNumber: numero,
      motivo: policy
        ? `pendiente con conducto ${canal?.raw}`
        : "no está en el reporte de pendientes",
    });
  }
}

output = {
  totalRecibosDelReporte: summary.receipts,
  pendientes,
  noEncontradas,
};
```

`noEncontradas` es la diferencia entre un resultado vacío y una respuesta: te
dice si la póliza ya no está pendiente o si lo está por otro conducto.

## Errores

El paso falla y detiene la ejecución si no puede iniciar sesión, si no puede
exportar el reporte, o si el archivo llega vacío o no es una hoja de cálculo.

Un cambio de columnas en GNP **no** lo hace fallar. Las columnas se localizan por
nombre, así que reordenarlas o agregar una no afecta nada, y la columna agregada
llega en el `rawFields` de cada recibo. Si desaparece una esperada, sus campos
quedan vacíos y `issues` recibe un `warning` con código
`GNP_REPORT_COLUMN_MISSING` que la nombra.

<Warning>
  El reporte y las grabaciones del navegador contienen nombres, pólizas y otros
  datos personales del portal. Limita el acceso a usuarios autorizados del
  tenant.
</Warning>

## Relacionado

* [Agentes preconfigurados para Seguros](/agentes/industrias/seguros)
* [Paso JavaScript](/automatizaciones/paso-javascript)
* [Archivos entre pasos](/automatizaciones/archivos-entre-pasos)
* [Crear automatización](/automatizaciones/crear-automatizacion)
