{
  "info": {
    "name": "TaxPilot API - Integrador",
    "description": "Colección para probar la API de TaxPilot (facturación Veri*FACTU) sin escribir código. Recorre el viaje del integrador: acceso, emisores, facturas, cuenta y herramientas públicas. Selecciona el entorno «TaxPilot - Integrador» y ejecuta las peticiones en orden: las variables {{jwt}} y {{apiKey}} se rellenan solas con los scripts de test.",
    "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
  },
  "item": [
    {
      "name": "1. Acceso",
      "item": [
        {
          "name": "Registrarse",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"email\": \"{{email}}\",\n  \"password\": \"{{password}}\",\n  \"acceptedTerms\": true,\n  \"acceptedPrivacy\": true\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "url": {
              "raw": "{{baseUrl}}/api/v1/auth/register",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "auth",
                "register"
              ]
            },
            "description": "Intenta crear una cuenta con email, contraseña y aceptación de los dos documentos. Durante la beta está cerrado a propósito: responde 403 con el texto «Public registration is currently disabled.», por lo que no devuelve ninguna alta utilizable. La guía lo documenta por completitud."
          },
          "response": []
        },
        {
          "name": "Iniciar sesion",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"email\": \"{{email}}\",\n  \"password\": \"{{password}}\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "url": {
              "raw": "{{baseUrl}}/api/v1/auth/login",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "auth",
                "login"
              ]
            },
            "description": "Autentica al usuario y devuelve un JWT en el campo `token`. Responde 200, o 400 por datos inválidos y 401 por credenciales incorrectas. El script de test guarda `token` en la variable de entorno `jwt` para las siguientes peticiones."
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "exec": [
                  "pm.test(\"Guarda el token\", function () {",
                  "    pm.collectionVariables.set(\"jwt\", pm.response.json().token);",
                  "});"
                ],
                "type": "text/javascript"
              }
            }
          ],
          "response": []
        },
        {
          "name": "Crear una API key",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Authorization",
                "value": "Bearer {{jwt}}"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"name\": \"produccion\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "url": {
              "raw": "{{baseUrl}}/api/v1/api-keys",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "api-keys"
              ]
            },
            "description": "Crea una credencial máquina a máquina para el usuario autenticado. Responde 201 con `plainTextKey` (solo se muestra aquí), además de `id`, `name`, `lastFourChars`, `createdAt` y `message`; 400 por petición inválida y 401 sin JWT. El script de test guarda `plainTextKey` en la variable de entorno `apiKey`."
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "exec": [
                  "pm.test(\"Guarda la API key\", function () {",
                  "    pm.collectionVariables.set(\"apiKey\", pm.response.json().plainTextKey);",
                  "});"
                ],
                "type": "text/javascript"
              }
            }
          ],
          "response": []
        }
      ]
    },
    {
      "name": "2. Emisores",
      "item": [
        {
          "name": "Dar de alta un emisor",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Authorization",
                "value": "Bearer {{jwt}}"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"nif\": \"{{nif}}\",\n  \"corporateName\": \"INTEGRADOR SL\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "url": {
              "raw": "{{baseUrl}}/api/v1/issuers",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "issuers"
              ]
            },
            "description": "Registra o actualiza un NIF emisor bajo la cuenta autenticada. El NIF debe tener nueve caracteres alfanuméricos y su dígito de control correcto. Responde 201 si crea el NIF y 200 si ese NIF ya era tuyo y solo se actualiza; 400 por datos o reglas de negocio inválidos, 401 sin credencial y 403 si no se puede acceder o aplicar el plan. La respuesta incluye `id`, `nif`, `corporateName`, `hasCertificate` y `modoVerifactu`."
          },
          "response": []
        },
        {
          "name": "Listar emisores",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Authorization",
                "value": "Bearer {{jwt}}"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/v1/issuers",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "issuers"
              ]
            },
            "description": "Lista los NIF emisores de la cuenta. Aviso: la guía solo nombra este endpoint (método y ruta) en el listado de métodos que no forman parte del recorrido documentado; no trae ejemplo de respuesta ni cabeceras, así que aquí se asume el JWT propio del resto de endpoints de emisores."
          },
          "response": []
        },
        {
          "name": "Ver un emisor",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Authorization",
                "value": "Bearer {{jwt}}"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/v1/issuers/{{nif}}",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "issuers",
                "{{nif}}"
              ]
            },
            "description": "Devuelve el detalle del emisor indicado. Aviso: la guía solo nombra este endpoint (método y ruta) en el listado de métodos que no forman parte del recorrido documentado; no trae ejemplo de respuesta ni cabeceras, así que aquí se asume el JWT propio del resto de endpoints de emisores."
          },
          "response": []
        },
        {
          "name": "Activar o desactivar Veri*FACTU",
          "request": {
            "method": "PATCH",
            "header": [
              {
                "key": "Authorization",
                "value": "Bearer {{jwt}}"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"modoVerifactu\": true\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "url": {
              "raw": "{{baseUrl}}/api/v1/issuers/{{nif}}/modo-verifactu",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "issuers",
                "{{nif}}",
                "modo-verifactu"
              ]
            },
            "description": "Activa o desactiva Veri*FACTU para el NIF indicado. Aviso: la guía solo nombra este endpoint (método y ruta) en el listado de métodos que no forman parte del recorrido documentado; no hay ejemplo del cuerpo ni de la respuesta. El campo `modoVerifactu` del cuerpo se ha tomado del campo homónimo que la guía documenta en la respuesta de alta de emisor."
          },
          "response": []
        }
      ]
    },
    {
      "name": "3. Facturas",
      "item": [
        {
          "name": "Emitir una factura (IVA 21%)",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "X-API-Key",
                "value": "{{apiKey}}"
              },
              {
                "key": "Idempotency-Key",
                "value": "3f7c1a90-6b2e-4f18-9a55-0c1d2e3f4a5b"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"series\": \"F2026\",\n  \"number\": \"0001\",\n  \"issueDate\": \"2026-09-05\",\n  \"issuer\": {\n    \"taxIdNumber\": \"{{nif}}\",\n    \"legalName\": \"INTEGRADOR SL\",\n    \"address\": {\"address\":\"Calle Mayor 1\",\"postCode\":\"28001\",\"town\":\"Madrid\",\"province\":\"Madrid\"}\n  },\n  \"receiver\": {\n    \"taxIdNumber\": \"A58818501\",\n    \"legalName\": \"CLIENTE DE PRUEBA SL\",\n    \"address\": {\"address\":\"Gran Via 2\",\"postCode\":\"28013\",\"town\":\"Madrid\",\"province\":\"Madrid\"}\n  },\n  \"lines\": [{\n    \"description\": \"Servicios de desarrollo de software\",\n    \"quantity\": 1.00,\n    \"unitPriceWithoutTax\": 100.00,\n    \"totalAmountWithoutTax\": 100.00,\n    \"taxRate\": 21.00\n  }],\n  \"totals\": {\n    \"totalGrossAmount\": 100.00,\n    \"totalTaxAmount\": 21.00,\n    \"totalPayableAmount\": 121.00\n  }\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "url": {
              "raw": "{{baseUrl}}/api/v1/facturae/json",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "facturae",
                "json"
              ]
            },
            "description": "Emite y encadena en Veri*FACTU una factura con IVA general del 21 %. Responde 201 con el registro (`id`, `numSerieFactura`, `huella`, `status` `READY_TO_SUBMIT`, `aeat` null mientras no haya envío). Cabeceras obligatorias `X-API-Key` (o JWT) y `Content-Type`; `Idempotency-Key` es opcional y aquí se envía un ejemplo. No se incluye `X-Certificate-Password` a propósito: solo hace falta si el emisor tiene un certificado subido y ese no es el camino normal. El test guarda el `id` en la variable de colección `registroId`."
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "exec": [
                  "pm.test(\"Guarda el id del registro\", function () {",
                  "    pm.collectionVariables.set(\"registroId\", pm.response.json().id);",
                  "});"
                ],
                "type": "text/javascript"
              }
            }
          ],
          "response": []
        },
        {
          "name": "Emitir una factura OSS (claveRegimen 17)",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "X-API-Key",
                "value": "{{apiKey}}"
              },
              {
                "key": "Idempotency-Key",
                "value": "b8d2e441-7c3a-4a6e-8f01-2b9c4d5e6f70"
              },
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"series\": \"F2026\",\n  \"number\": \"0002\",\n  \"issueDate\": \"2026-09-05\",\n  \"issuer\": {\n    \"taxIdNumber\": \"{{nif}}\",\n    \"legalName\": \"INTEGRADOR SL\",\n    \"address\": {\"address\":\"Calle Mayor 1\",\"postCode\":\"28001\",\"town\":\"Madrid\",\"province\":\"Madrid\"}\n  },\n  \"receiver\": {\n    \"taxIdNumber\": \"A58818501\",\n    \"legalName\": \"CLIENTE DE PRUEBA SL\",\n    \"address\": {\"address\":\"Gran Via 2\",\"postCode\":\"28013\",\"town\":\"Madrid\",\"province\":\"Madrid\"}\n  },\n  \"lines\": [{\n    \"description\": \"Servicios de desarrollo de software\",\n    \"quantity\": 1.00,\n    \"unitPriceWithoutTax\": 100.00,\n    \"totalAmountWithoutTax\": 100.00,\n    \"taxRate\": 0.00,\n    \"claveRegimen\": \"17\",\n    \"calificacion\": \"N2\"\n  }],\n  \"totals\": {\n    \"totalGrossAmount\": 100.00,\n    \"totalTaxAmount\": 0.00,\n    \"totalPayableAmount\": 100.00\n  }\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "url": {
              "raw": "{{baseUrl}}/api/v1/facturae/json",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "facturae",
                "json"
              ]
            },
            "description": "Emite una factura acogida a ventanilla única OSS/IOSS, marcando la línea con `claveRegimen` `17` y `calificacion` `N2`. Al ser una operación no sujeta por localización no lleva IVA español (`taxRate` 0), tal como exige la validación 1237 de la AEAT. Se usa otra `Idempotency-Key` distinta de la de la factura anterior: reutilizar una clave con un cuerpo diferente devuelve 422. El test guarda el `id` en `registroId`."
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "exec": [
                  "pm.test(\"Guarda el id del registro\", function () {",
                  "    pm.collectionVariables.set(\"registroId\", pm.response.json().id);",
                  "});"
                ],
                "type": "text/javascript"
              }
            }
          ],
          "response": []
        },
        {
          "name": "Consultar el registro y lo que dijo la AEAT",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "X-API-Key",
                "value": "{{apiKey}}"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/v1/registros/{{registroId}}",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "registros",
                "{{registroId}}"
              ]
            },
            "description": "Devuelve el estado interno del registro y el acuse del último envío a AEAT. `aeat` es null si nunca se envió; cuando existe trae `csv`, `estadoEnvio`, `estadoRegistro`, `codigoError`, `descripcionError`, `enviadoEn` y `respuestaRecibidaEn`. Usa el `id` que dejó guardado la emisión en la variable de colección `registroId`. Responde 200; 401 sin credencial, 403 si el registro es de otra cuenta y 404 si no existe."
          },
          "response": []
        },
        {
          "name": "Anular una factura",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "X-API-Key",
                "value": "{{apiKey}}"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/v1/registros/{{registroId}}/anulacion",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "registros",
                "{{registroId}}",
                "anulacion"
              ]
            },
            "description": "Anula la factura indicada. Anular NO borra nada: crea un registro nuevo de tipo ANULACION que se encadena a la huella anterior y se declara a Hacienda como el resto. Un 201 significa «encadenada y guardada», NO «Hacienda ya lo sabe»: el `status` viene en `READY_TO_SUBMIT` y el envío real se consulta después usando el `id` devuelto (que es el del registro de anulación, no el de la factura). Solo se pueden anular facturas aceptadas por la AEAT o guardadas sin declarar. Responde 201 con el registro de anulación (`id`, `registroAnuladoId`, `idEmisorFactura`, `numSerieFacturaAnulada`, `fechaExpedicionFacturaAnulada`, `huella` y `status`); 400 si no se puede anular (ya está anulada, ya es una anulación o su estado no lo permite, el motivo viene en `message`), 401 sin credencial, 403 si la factura es de otra cuenta y 404 si no existe. Usa el `id` guardado en la variable de colección `registroId`."
          },
          "response": []
        }
      ]
    },
    {
      "name": "4. Cuenta",
      "item": [
        {
          "name": "Ver mi plan y lo que me queda",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Authorization",
                "value": "Bearer {{jwt}}"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/v1/user/plan",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "user",
                "plan"
              ]
            },
            "description": "Consulta el estado del plan del usuario y su consumo. Aviso: la guía solo nombra este endpoint (método y ruta) en el listado de métodos que no forman parte del recorrido documentado, sin ejemplo de respuesta ni cabeceras."
          },
          "response": []
        },
        {
          "name": "Ver mi perfil",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Authorization",
                "value": "Bearer {{jwt}}"
              }
            ],
            "url": {
              "raw": "{{baseUrl}}/api/v1/users/profile",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "v1",
                "users",
                "profile"
              ]
            },
            "description": "Devuelve el perfil del usuario autenticado. Aviso: la guía solo nombra este endpoint (método y ruta) en el listado de métodos que no forman parte del recorrido documentado, sin ejemplo de respuesta ni cabeceras."
          },
          "response": []
        }
      ]
    },
    {
      "name": "5. Herramientas publicas (sin credenciales)",
      "item": [
        {
          "name": "Ver los planes y precios",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/api/public/plans",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "api",
                "public",
                "plans"
              ]
            },
            "description": "Devuelve el catálogo público de planes y precios. No requiere credenciales. Aviso: la guía solo nombra este endpoint (método y ruta) en el listado de métodos que no forman parte del recorrido documentado, sin ejemplo de respuesta."
          },
          "response": []
        },
        {
          "name": "Ping",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/ping",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "ping"
              ]
            },
            "description": "Comprobación de salud del servicio. No requiere credenciales: sirve para verificar que la API responde antes de empezar el recorrido."
          },
          "response": []
        }
      ]
    }
  ],
  "variable": [
    {
      "key": "jwt",
      "value": "",
      "type": "string"
    },
    {
      "key": "apiKey",
      "value": "",
      "type": "string"
    },
    {
      "key": "registroId",
      "value": "",
      "type": "string"
    }
  ]
}
