From 39e21878f1a46f38eab5c6e0b372e29c793887d8 Mon Sep 17 00:00:00 2001 From: DevEnv nis2-agile Date: Sat, 7 Mar 2026 16:05:49 +0100 Subject: [PATCH] [MKTG-API] Fix: Postman collection in public root --- public/mktg-api-doc.html | 4 +- public/nis2-license-api.postman.json | 259 +++++++++++++++++++++++++++ 2 files changed, 261 insertions(+), 2 deletions(-) create mode 100644 public/nis2-license-api.postman.json diff --git a/public/mktg-api-doc.html b/public/mktg-api-doc.html index 8dc30b1..6994ac6 100644 --- a/public/mktg-api-doc.html +++ b/public/mktg-api-doc.html @@ -94,7 +94,7 @@ table.resp code { background: rgba(255,255,255,.06); padding: .1rem .3rem; borde HTTPS API Key v1.0 - + ⬇ Postman Collection (JSON) @@ -356,7 +356,7 @@ curl https://nis2.agile.software/api/services/status
NIS2 Agile v1.0 · Pannello Licenze · Guida Partner · - Postman Collection + Postman Collection
diff --git a/public/nis2-license-api.postman.json b/public/nis2-license-api.postman.json new file mode 100644 index 0000000..27b9eea --- /dev/null +++ b/public/nis2-license-api.postman.json @@ -0,0 +1,259 @@ +{ + "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." + } + } + ] + } + ] +}