{
  "info": {
    "name": "NIS2 Agile — License API",
    "description": "API per la gestione licenze NIS2 Agile da sistemi esterni (mktg-agile, e-commerce, partner).\n\nAutenticazione: API Key con scope `admin:licenses`\nHeader: `X-API-Key: nis2_xxxx`\n\nPer ottenere una chiave: login su https://nis2.agile.software → licenseExt.html → login → Settings → API Keys → scope: admin:licenses",
    "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json",
    "_postman_id": "nis2-license-api-v1"
  },
  "variable": [
    { "key": "base_url", "value": "https://nis2.agile.software/api", "type": "string" },
    { "key": "api_key",  "value": "nis2_LA_TUA_CHIAVE_QUI",          "type": "string" },
    { "key": "jwt",      "value": "",                                  "type": "string" },
    { "key": "invite_id","value": "1",                                 "type": "string" }
  ],
  "item": [
    {
      "name": "0. Auth — Ottieni JWT (alternativa all'API Key)",
      "item": [
        {
          "name": "POST /auth/login",
          "event": [
            {
              "listen": "test",
              "script": {
                "exec": [
                  "const d = pm.response.json();",
                  "if (d.success) {",
                  "  pm.collectionVariables.set('jwt', d.data.access_token);",
                  "  console.log('JWT salvato:', d.data.access_token.substring(0,30) + '...');",
                  "}"
                ],
                "type": "text/javascript"
              }
            }
          ],
          "request": {
            "method": "POST",
            "header": [{ "key": "Content-Type", "value": "application/json" }],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"email\": \"cristiano.benassati@gmail.com\",\n  \"password\": \"Silvia1978!@\"\n}"
            },
            "url": { "raw": "{{base_url}}/auth/login", "host": ["{{base_url}}"], "path": ["auth","login"] },
            "description": "Ottieni JWT super_admin (valido 2h). Alternativa all'API Key per test manuali.\nIl JWT viene salvato automaticamente nella variabile {{jwt}}."
          }
        }
      ]
    },
    {
      "name": "1. Crea Licenza",
      "item": [
        {
          "name": "POST /invites/create — Licenza singola professional 12m",
          "event": [
            {
              "listen": "test",
              "script": {
                "exec": [
                  "const d = pm.response.json();",
                  "pm.test('Status 201', () => pm.response.to.have.status(201));",
                  "pm.test('Contiene token', () => pm.expect(d.data.invites[0].token).to.match(/^inv_/));",
                  "if (d.success && d.data.invites.length) {",
                  "  pm.collectionVariables.set('invite_id', d.data.invites[0].id);",
                  "  console.log('TOKEN (salva subito!):', d.data.invites[0].token);",
                  "  console.log('URL onboarding:', d.data.invites[0].invite_url);",
                  "}"
                ],
                "type": "text/javascript"
              }
            }
          ],
          "request": {
            "method": "POST",
            "header": [
              { "key": "Content-Type",  "value": "application/json" },
              { "key": "X-API-Key",     "value": "{{api_key}}" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"plan\": \"professional\",\n  \"duration_months\": 12,\n  \"invite_expires_days\": 30,\n  \"max_uses\": 1,\n  \"max_users_per_org\": 10,\n  \"label\": \"NIS2 Professional — Ordine MKT-2026-001\",\n  \"channel\": \"ecommerce\",\n  \"issued_to\": \"cliente@azienda.it\",\n  \"reseller_name\": \"mktg-agile S.r.l.\",\n  \"price_eur\": 990.00,\n  \"notes\": \"Campagna Q1 2026\"\n}"
            },
            "url": { "raw": "{{base_url}}/invites/create", "host": ["{{base_url}}"], "path": ["invites","create"] },
            "description": "Crea una licenza singola Professional 12 mesi.\n\nRESPONSE (201):\n- `invites[].token` — inv_xxx... → SALVARE SUBITO, non recuperabile\n- `invites[].invite_url` — URL per attivare da browser\n- `invites[].expires_at` — scadenza invito (entro cui attivare)\n- `warning` — promemoria sicurezza token"
          }
        },
        {
          "name": "POST /invites/create — Batch 5 licenze Essentials",
          "request": {
            "method": "POST",
            "header": [
              { "key": "Content-Type", "value": "application/json" },
              { "key": "X-API-Key",    "value": "{{api_key}}" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"plan\": \"essentials\",\n  \"duration_months\": 6,\n  \"invite_expires_days\": 60,\n  \"max_uses\": 1,\n  \"max_users_per_org\": 3,\n  \"quantity\": 5,\n  \"label\": \"Essentials 6m — Bundle Reseller Q1\",\n  \"channel\": \"reseller\",\n  \"reseller_name\": \"Partner XYZ\",\n  \"price_eur\": 490.00,\n  \"notes\": \"Batch 5 licenze per partner XYZ\"\n}"
            },
            "url": { "raw": "{{base_url}}/invites/create", "host": ["{{base_url}}"], "path": ["invites","create"] },
            "description": "Genera 5 token in un'unica chiamata (quantity=5).\nRisposta contiene array `invites` con 5 elementi, ciascuno con token unico."
          }
        },
        {
          "name": "POST /invites/create — Enterprise con restrizione P.IVA",
          "request": {
            "method": "POST",
            "header": [
              { "key": "Content-Type", "value": "application/json" },
              { "key": "X-API-Key",    "value": "{{api_key}}" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"plan\": \"enterprise\",\n  \"duration_months\": 24,\n  \"invite_expires_days\": 14,\n  \"max_uses\": 1,\n  \"label\": \"Enterprise 24m — Cliente Acme S.r.l.\",\n  \"channel\": \"direct\",\n  \"issued_to\": \"ciso@acme.it\",\n  \"restrict_vat\": \"02345678901\",\n  \"restrict_email\": \"ciso@acme.it\",\n  \"price_eur\": 4800.00,\n  \"notes\": \"Contratto enterprise diretto — approvato da DG\"\n}"
            },
            "url": { "raw": "{{base_url}}/invites/create", "host": ["{{base_url}}"], "path": ["invites","create"] },
            "description": "Licenza Enterprise riservata a una specifica P.IVA e email admin.\nSe usata da P.IVA diversa → errore 403 INVITE_VAT_MISMATCH."
          }
        }
      ]
    },
    {
      "name": "2. Lista Licenze",
      "item": [
        {
          "name": "GET /invites/list — tutte",
          "request": {
            "method": "GET",
            "header": [{ "key": "X-API-Key", "value": "{{api_key}}" }],
            "url": {
              "raw": "{{base_url}}/invites/list?limit=50",
              "host": ["{{base_url}}"],
              "path": ["invites","list"],
              "query": [
                { "key": "limit",  "value": "50" },
                { "key": "offset", "value": "0", "disabled": true }
              ]
            },
            "description": "Lista tutte le licenze. Filtrabile per status e channel.\nCampi risposta: id, token_prefix, plan, status, used_count, max_uses, max_users_per_org, price_eur, reseller_name, expires_at, channel, label, issued_to, used_by_org_id."
          }
        },
        {
          "name": "GET /invites/list — solo pending (attive)",
          "request": {
            "method": "GET",
            "header": [{ "key": "X-API-Key", "value": "{{api_key}}" }],
            "url": {
              "raw": "{{base_url}}/invites/list?status=pending&limit=100",
              "host": ["{{base_url}}"],
              "path": ["invites","list"],
              "query": [
                { "key": "status", "value": "pending" },
                { "key": "limit",  "value": "100" }
              ]
            }
          }
        },
        {
          "name": "GET /invites/list — per canale ecommerce",
          "request": {
            "method": "GET",
            "header": [{ "key": "X-API-Key", "value": "{{api_key}}" }],
            "url": {
              "raw": "{{base_url}}/invites/list?channel=ecommerce&status=pending",
              "host": ["{{base_url}}"],
              "path": ["invites","list"],
              "query": [
                { "key": "channel", "value": "ecommerce" },
                { "key": "status",  "value": "pending" }
              ]
            }
          }
        },
        {
          "name": "GET /invites/{id} — dettaglio singolo",
          "request": {
            "method": "GET",
            "header": [{ "key": "X-API-Key", "value": "{{api_key}}" }],
            "url": {
              "raw": "{{base_url}}/invites/{{invite_id}}",
              "host": ["{{base_url}}"],
              "path": ["invites","{{invite_id}}"]
            },
            "description": "Dettaglio completo. Se usata, include used_by_org con name/sector/nis2_entity_type."
          }
        }
      ]
    },
    {
      "name": "3. Revoca / Rigenera",
      "item": [
        {
          "name": "DELETE /invites/{id} — revoca",
          "request": {
            "method": "DELETE",
            "header": [{ "key": "X-API-Key", "value": "{{api_key}}" }],
            "url": {
              "raw": "{{base_url}}/invites/{{invite_id}}",
              "host": ["{{base_url}}"],
              "path": ["invites","{{invite_id}}"]
            },
            "description": "Revoca la licenza. Operazione non reversibile.\nLa licenza passa a status=revoked. Eventuali aziende già provisionate rimangono attive (il provisioning è già avvenuto).\nNon si può revocare una licenza già usata (status=used)."
          }
        },
        {
          "name": "POST /invites/{id}/regenerate — nuovo token",
          "request": {
            "method": "POST",
            "header": [{ "key": "X-API-Key", "value": "{{api_key}}" }],
            "url": {
              "raw": "{{base_url}}/invites/{{invite_id}}/regenerate",
              "host": ["{{base_url}}"],
              "path": ["invites","{{invite_id}}","regenerate"]
            },
            "description": "Genera un nuovo token invalidando il vecchio. Utile se il token è stato inviato per errore.\nRisposta: { id, token, token_prefix, warning }. Il nuovo token è visibile una sola volta."
          }
        }
      ]
    },
    {
      "name": "4. Validazione pubblica (no auth)",
      "item": [
        {
          "name": "GET /invites/validate?token= — anteprima invito",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/invites/validate?token=inv_INSERISCI_TOKEN_QUI",
              "host": ["{{base_url}}"],
              "path": ["invites","validate"],
              "query": [{ "key": "token", "value": "inv_INSERISCI_TOKEN_QUI" }]
            },
            "description": "Endpoint pubblico — nessuna auth richiesta.\nUsato da lg231 prima del provisioning e dalla pagina onboarding per mostrare l'anteprima piano.\nRisposta: { valid, plan, duration_months, expires_at, remaining_uses, max_users_per_org, plan_features[] }"
          }
        }
      ]
    },
    {
      "name": "5. Provisioning (per lg231 / e-commerce)",
      "item": [
        {
          "name": "POST /services/provision — attivazione con invite_token",
          "request": {
            "method": "POST",
            "header": [{ "key": "Content-Type", "value": "application/json" }],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"invite_token\": \"inv_INSERISCI_TOKEN_RICEVUTO\",\n  \"company\": {\n    \"ragione_sociale\": \"Acme S.r.l.\",\n    \"partita_iva\": \"02345678901\",\n    \"ateco_code\": \"62.01.00\",\n    \"sector\": \"ict\"\n  },\n  \"admin\": {\n    \"email\": \"ciso@acme.it\",\n    \"first_name\": \"Marco\",\n    \"last_name\": \"Rossi\"\n  },\n  \"caller\": {\n    \"system\": \"mktg-agile\",\n    \"callback_url\": \"https://mktg.agile.software/api/webhooks/nis2-provisioned\"\n  }\n}"
            },
            "url": {
              "raw": "{{base_url}}/services/provision",
              "host": ["{{base_url}}"],
              "path": ["services","provision"]
            },
            "description": "Attiva automaticamente una licenza usando il token ricevuto.\nNON richiede X-API-Key — l'invite_token è l'auth.\nRisposta: org_id, api_key, access_token (JWT 2h), temp_password, license_expires_at, dashboard_url."
          }
        }
      ]
    }
  ]
}
