nis2-agile/public/nis2-license-api.postman.json
2026-03-07 16:05:49 +01:00

260 lines
12 KiB
JSON

{
"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."
}
}
]
}
]
}