260 lines
12 KiB
JSON
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."
|
|
}
|
|
}
|
|
]
|
|
}
|
|
]
|
|
}
|