{"openapi":"3.0.0","info":{"title":"MARTA Platform 2.0 API","description":"REST API per MARTA Platform - Loyalty Platform\n\n### Viste per tipo di token\n- [/docs/customer](/docs/customer): operazioni aperte al JWT `operatorType=CUSTOMER`.\n- [/docs/terminal](/docs/terminal): operazioni aperte al JWT `operatorType=POS` (login `POST /oauth/terminal`).\n\n### Formato errori\nTutte le risposte di errore usano il wrapper condiviso **ApiErrorEnvelope**: `{ \"error\": { \"code\", \"message\", \"language?\", \"messages?\", \"path?\", \"timestamp?\" }, \"errordescrition?\" }` (vedi *Schemas*).\n`error.message` è nella lingua del token (operatore/cliente) o, in assenza, di `Accept-Language` / `X-Language` (IT, EN, ES). Sotto il messaggio, salvo eccezioni UI POS, sono riportate le altre lingue gestite; `error.messages` contiene le tre traduzioni strutturate.\nPer ogni operazione, le risposte **400**, **401**, **403**, **404**, **409** e **500** (ove presenti) includono descrizione dei codici HTTP e un esempio JSON; i valori effettivi di `error.code` dipendono dall’endpoint e dalla logica applicativa.","version":"1.1.0","contact":{"name":"MARTA Platform"}},"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"},"apiKey":{"type":"apiKey","name":"X-API-Key","in":"header"}},"schemas":{"ApiErrorEnvelope":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"Codice stabile (es. UNAUTHORIZED, PROGRAM_NOT_FOUND)"},"message":{"type":"string","description":"Messaggio nella lingua del token (operatore/cliente) o Accept-Language; sotto le altre lingue gestite (IT, EN, ES)."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","description":"Traduzioni in tutte le lingue gestite","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message"}},"required":["error"],"additionalProperties":true}}},"paths":{"/health":{"get":{"tags":["Health"],"description":"Verifica lo stato del servizio API e la connettività al database.\n\n**Contesto:** Endpoint di monitoraggio infrastruttura: verificano che l'API risponda e, dove previsto, che il database sia raggiungibile.\n\n**Autenticazione:** endpoint pubblico — non richiede `Authorization: Bearer` (può richiedere `X-API-Key` e/o OTP a seconda del caso).\n\n**Risposte di successo:**\n- **200**: Default Response\n\n**Errori documentati:** 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Health.","responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string"},"service":{"type":"string"},"version":{"type":"string"},"timestamp":{"type":"string"}},"example":{"status":"ok","service":"marta-api","version":"1.1.3","timestamp":"2026-04-03T12:00:00.000Z"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/health":{"get":{"tags":["Health"],"description":"Verifica lo stato del servizio API e la connettività al database.\n\n**Contesto:** Endpoint di monitoraggio infrastruttura: verificano che l'API risponda e, dove previsto, che il database sia raggiungibile.\n\n**Autenticazione:** endpoint pubblico — non richiede `Authorization: Bearer` (può richiedere `X-API-Key` e/o OTP a seconda del caso).\n\n**Risposte di successo:**\n- **200**: Default Response\n\n**Errori documentati:** 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Health.","responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string"},"service":{"type":"string"},"version":{"type":"string"},"database":{"type":"string"},"timestamp":{"type":"string"}},"example":{"status":"ok","service":"marta-api","version":"1.1.3","database":"connected","timestamp":"2026-04-03T12:00:00.000Z"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/oauth":{"post":{"tags":["Auth"],"description":"Login with username and password. Returns access_token (10 min) and refresh_token (7 days).\n\n**Contesto:** Autenticazione OAuth: scambio credenziali (operatore, cliente o terminale POS) con API Key e rilascio JWT (access + refresh token).\n\n**Autenticazione:** endpoint pubblico — non richiede `Authorization: Bearer` (può richiedere `X-API-Key` e/o OTP a seconda del caso).\n\n**Body JSON:**\n- `username` (string) — obbligatorio\n- `password` (string) — obbligatorio\n- `devicetype` (string) — opzionale: Canale: WEB, APP iOS, APP Android, WEBADMIN o ANDROID (salvato nel JWT e usato per filtri promo e movimenti) (valori: WEB, APP iOS, APP Android, WEBADMIN, ANDROID)\n\n**Risposte di successo:**\n- **200**: Default Response\n\n**Errori documentati:** 400, 401, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Auth.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["username","password"],"properties":{"username":{"type":"string","example":"operator1"},"password":{"type":"string","format":"password"},"devicetype":{"type":"string","enum":["WEB","APP","APP iOS","APP Android","WEBADMIN","ANDROID","AI_CONNECTOR"],"description":"Canale: WEB, APP, APP iOS, APP Android, WEBADMIN o ANDROID (salvato nel JWT e usato per filtri promo e movimenti)"}}}}},"required":true},"parameters":[{"schema":{"type":"string"},"in":"header","name":"x-api-key","required":false,"description":"API Key (optional)"}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"access_token":{"type":"string"},"token_type":{"type":"string","example":"Bearer"},"expires_in":{"type":"number"},"refresh_token":{"type":"string","nullable":true},"refresh_expires_at":{"type":"string","format":"date-time","nullable":true},"user":{"type":"object","properties":{"id":{"type":"integer"},"username":{"type":"string"},"email":{"type":"string","nullable":true},"firstName":{"type":"string","nullable":true},"lastName":{"type":"string","nullable":true},"customerToken":{"type":"string","nullable":true},"programId":{"type":"integer","nullable":true},"programIds":{"type":"array","nullable":true,"items":{"type":"integer"},"description":"Programas que el operador puede abrir. null = todos (isSystem)."},"programName":{"type":"string","nullable":true},"isSystem":{"type":"boolean"},"roles":{"type":"array","items":{"type":"string"}},"deviceType":{"type":"integer","description":"1=WEB, 2=APP iOS, 3=APP Android, 4=WEBADMIN, 5=ANDROID","nullable":true},"mustChangePassword":{"type":"boolean"},"language":{"type":"string","enum":["it","en","es"]}}}},"example":{"access_token":"","token_type":"Bearer","expires_in":0,"refresh_token":"","refresh_expires_at":"","user":{"id":0,"username":"","email":"","firstName":"","lastName":"","customerToken":"","programId":0,"programIds":[0],"programName":"","isSystem":false,"roles":[""],"deviceType":0,"mustChangePassword":false,"language":"it"}}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"Credenziali operatore non valide. Codice: `INVALID_CREDENTIALS`.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}}}},"description":"Credenziali operatore non valide. Codice: `INVALID_CREDENTIALS`.","example":{"error":{"code":"INVALID_CREDENTIALS","message":"Username or password invalid","language":"it","messages":{"it":"Username or password invalid","en":"Username or password invalid","es":"Username or password invalid"},"path":"/oauth","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Username or password invalid"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/oauth/customer":{"post":{"tags":["Auth"],"description":"Login customer with username, password and programId. Sets operatorType=CUSTOMER in JWT.\n\n**Contesto:** Autenticazione OAuth: scambio credenziali (operatore, cliente o terminale POS) con API Key e rilascio JWT (access + refresh token).\n\n**Autenticazione:** endpoint pubblico — non richiede `Authorization: Bearer` (può richiedere `X-API-Key` e/o OTP a seconda del caso).\n\n**Body JSON:**\n- `programId` (integer) — obbligatorio\n- `username` (string) — obbligatorio\n- `password` (string) — obbligatorio\n- `devicetype` (string) — opzionale: Canale: WEB, APP iOS, APP Android, WEBADMIN o ANDROID (salvato nel JWT e usato per filtri promo e movimenti) (valori: WEB, APP iOS, APP Android, WEBADMIN, ANDROID)\n\n**Risposte di successo:**\n- **200**: Default Response\n\n**Errori documentati:** 400, 401, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Auth.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["programId","username","password"],"properties":{"programId":{"type":"integer","example":1},"username":{"type":"string","example":"customer1"},"password":{"type":"string","format":"password"},"devicetype":{"type":"string","enum":["WEB","APP","APP iOS","APP Android","WEBADMIN","ANDROID","AI_CONNECTOR"],"description":"Canale: WEB, APP, APP iOS, APP Android, WEBADMIN o ANDROID (salvato nel JWT e usato per filtri promo e movimenti)"}}}}},"required":true},"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"access_token":{"type":"string"},"token_type":{"type":"string","example":"Bearer"},"expires_in":{"type":"number"},"refresh_token":{"type":"string","nullable":true,"description":"Refresh token (30 giorni)"},"refresh_expires_at":{"type":"string","format":"date-time","nullable":true},"user":{"type":"object","properties":{"id":{"type":"integer"},"username":{"type":"string"},"email":{"type":"string","nullable":true},"firstName":{"type":"string","nullable":true},"lastName":{"type":"string","nullable":true},"customerToken":{"type":"string","nullable":true},"programId":{"type":"integer"},"operatorType":{"type":"string","example":"CUSTOMER"},"deviceType":{"type":"integer","nullable":true}}}},"example":{"access_token":"","token_type":"Bearer","expires_in":0,"refresh_token":"","refresh_expires_at":"","user":{"id":0,"username":"","email":"","firstName":"","lastName":"","customerToken":"","programId":0,"operatorType":"CUSTOMER","deviceType":0}}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"Login cliente rifiutato. Codici: `INVALID_CREDENTIALS`, `ACCESS_NOT_ACTIVATED` (accesso non attivato).","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}}}},"description":"Login cliente rifiutato. Codici: `INVALID_CREDENTIALS`, `ACCESS_NOT_ACTIVATED` (accesso non attivato).","example":{"error":{"code":"ACCESS_NOT_ACTIVATED","message":"Customer access is not activated","language":"it","messages":{"it":"Customer access is not activated","en":"Customer access is not activated","es":"Customer access is not activated"},"path":"/oauth/customer","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Customer access is not activated"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/oauth/terminal":{"post":{"tags":["Auth"],"description":"Login POS terminal: username + password + (terminalCode oppure serialNumber). Sets operatorType=POS in JWT.\n\n**Contesto:** Autenticazione OAuth: scambio credenziali (operatore, cliente o terminale POS) con API Key e rilascio JWT (access + refresh token).\n\n**Autenticazione:** endpoint pubblico — non richiede `Authorization: Bearer` (può richiedere `X-API-Key` e/o OTP a seconda del caso).\n\n**Body JSON:**\n- `username` (string) — obbligatorio\n- `password` (string) — obbligatorio\n- `devicetype` (string) — opzionale: Canale: WEB, APP iOS, APP Android, WEBADMIN o ANDROID (salvato nel JWT e usato per filtri promo e movimenti) (valori: WEB, APP iOS, APP Android, WEBADMIN, ANDROID)\n- `serialNumber` (string) — opzionale: Serial number hardware del POS (alternativa a terminalCode)\n- `terminalCode` (string) — opzionale: Codice terminale (8 caratteri); alternativo a serialNumber\n\n**Risposte di successo:**\n- **200**: Default Response\n\n**Errori documentati:** 400, 401, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Auth.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["username","password"],"properties":{"username":{"type":"string","example":"pos1"},"password":{"type":"string","format":"password"},"devicetype":{"type":"string","enum":["WEB","APP","APP iOS","APP Android","WEBADMIN","ANDROID","AI_CONNECTOR"],"description":"Canale: WEB, APP, APP iOS, APP Android, WEBADMIN o ANDROID (salvato nel JWT e usato per filtri promo e movimenti)"},"terminalCode":{"type":"string","minLength":1,"maxLength":64,"description":"Codice terminale oppure numero seriale. Si cerca in entrambe le colonne."},"serialNumber":{"type":"string","minLength":1,"maxLength":64,"description":"Serial number hardware del POS (es. PAX SN). Alternativa a terminalCode."}}}}},"required":true},"parameters":[{"schema":{"type":"string"},"in":"header","name":"x-api-key","required":false,"description":"API Key (optional)"}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"access_token":{"type":"string"},"token_type":{"type":"string","example":"Bearer"},"expires_in":{"type":"number"},"refresh_token":{"type":"string","nullable":true},"refresh_expires_at":{"type":"string","format":"date-time","nullable":true},"user":{"type":"object","properties":{"id":{"type":"integer"},"username":{"type":"string"},"email":{"type":"string","nullable":true},"firstName":{"type":"string","nullable":true},"lastName":{"type":"string","nullable":true},"customerToken":{"type":"string","nullable":true},"programId":{"type":"integer","nullable":true},"programIds":{"type":"array","nullable":true,"items":{"type":"integer"},"description":"Programas que el operador puede abrir. null = todos (isSystem)."},"programName":{"type":"string","nullable":true},"isSystem":{"type":"boolean"},"roles":{"type":"array","items":{"type":"string"}},"deviceType":{"type":"integer","description":"1=WEB, 2=APP iOS, 3=APP Android, 4=WEBADMIN, 5=ANDROID","nullable":true},"mustChangePassword":{"type":"boolean"},"language":{"type":"string","enum":["it","en","es"]}}}},"example":{"access_token":"","token_type":"Bearer","expires_in":0,"refresh_token":"","refresh_expires_at":"","user":{"id":0,"username":"","email":"","firstName":"","lastName":"","customerToken":"","programId":0,"programIds":[0],"programName":"","isSystem":false,"roles":[""],"deviceType":0,"mustChangePassword":false,"language":"it"}}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"Login terminale/POS rifiutato. Codici: `INVALID_CREDENTIALS`, `TERMINAL_CODE_REQUIRED`, `TERMINAL_CODE_NOT_FOUND`, `TERMINAL_CONTEXT_DISABLED`, `TERMINAL_OPERATOR_SCOPE_DENIED`, `API_KEY_INVALID`, `API_KEY_PROGRAM_MISMATCH` (ultimi due se invii `X-API-Key`).","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","description":"INVALID_CREDENTIALS | TERMINAL_IDENTIFIER_REQUIRED | TERMINAL_CODE_REQUIRED | TERMINAL_CODE_NOT_FOUND | TERMINAL_SERIAL_NOT_FOUND | TERMINAL_CONTEXT_DISABLED | TERMINAL_OPERATOR_SCOPE_DENIED | API_KEY_INVALID | API_KEY_PROGRAM_MISMATCH"},"message":{"type":"string"}},"required":["code","message"],"additionalProperties":true}},"required":["error"],"description":"Login terminale/POS rifiutato. Codici: `INVALID_CREDENTIALS`, `TERMINAL_CODE_REQUIRED`, `TERMINAL_CODE_NOT_FOUND`, `TERMINAL_CONTEXT_DISABLED`, `TERMINAL_OPERATOR_SCOPE_DENIED`, `API_KEY_INVALID`, `API_KEY_PROGRAM_MISMATCH` (ultimi due se invii `X-API-Key`).","example":{"error":{"code":"TERMINAL_CODE_NOT_FOUND","message":"No active terminal matches this terminalCode","language":"it","messages":{"it":"No active terminal matches this terminalCode","en":"No active terminal matches this terminalCode","es":"No active terminal matches this terminalCode"},"path":"/oauth/terminal","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"No active terminal matches this terminalCode"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/oauth/terminal/tag":{"post":{"tags":["Auth"],"description":"Login POS via tag NFC operatore (UID). Richiede serialNumber o terminalCode. Flag businessRules.nfcTags.operatorLoginEnabled.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["nfcUid"],"properties":{"nfcUid":{"type":"string","minLength":4,"maxLength":40},"terminalCode":{"type":"string","minLength":1,"maxLength":64,"description":"Codice terminale oppure numero seriale. Si cerca in entrambe le colonne."},"serialNumber":{"type":"string","minLength":1,"maxLength":64,"description":"Serial number hardware del POS."},"devicetype":{"type":"string","enum":["WEB","APP","APP iOS","APP Android","WEBADMIN","ANDROID","AI_CONNECTOR"],"description":"Canale: WEB, APP, APP iOS, APP Android, WEBADMIN o ANDROID (salvato nel JWT e usato per filtri promo e movimenti)"}}}}},"required":true},"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"access_token":{"type":"string"},"token_type":{"type":"string","example":"Bearer"},"expires_in":{"type":"number"},"refresh_token":{"type":"string","nullable":true},"refresh_expires_at":{"type":"string","format":"date-time","nullable":true},"user":{"type":"object","properties":{"id":{"type":"integer"},"username":{"type":"string"},"email":{"type":"string","nullable":true},"firstName":{"type":"string","nullable":true},"lastName":{"type":"string","nullable":true},"customerToken":{"type":"string","nullable":true},"programId":{"type":"integer","nullable":true},"programIds":{"type":"array","nullable":true,"items":{"type":"integer"},"description":"Programas que el operador puede abrir. null = todos (isSystem)."},"programName":{"type":"string","nullable":true},"isSystem":{"type":"boolean"},"roles":{"type":"array","items":{"type":"string"}},"deviceType":{"type":"integer","description":"1=WEB, 2=APP iOS, 3=APP Android, 4=WEBADMIN, 5=ANDROID","nullable":true},"mustChangePassword":{"type":"boolean"},"language":{"type":"string","enum":["it","en","es"]}}}},"example":{"access_token":"","token_type":"Bearer","expires_in":0,"refresh_token":"","refresh_expires_at":"","user":{"id":0,"username":"","email":"","firstName":"","lastName":"","customerToken":"","programId":0,"programIds":[0],"programName":"","isSystem":false,"roles":[""],"deviceType":0,"mustChangePassword":false,"language":"it"}}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/oauth/refresh":{"post":{"tags":["Auth"],"description":"Rinnova il token JWT tramite refresh token.\n\n**Contesto:** Autenticazione OAuth: scambio credenziali (operatore, cliente o terminale POS) con API Key e rilascio JWT (access + refresh token).\n\n**Autenticazione:** endpoint pubblico — non richiede `Authorization: Bearer` (può richiedere `X-API-Key` e/o OTP a seconda del caso).\n\n**Body JSON:**\n- `refresh_token` (string) — obbligatorio\n- `devicetype` (string) — opzionale: Opzionale: canale per il nuovo token (come in POST /oauth) (valori: WEB, APP iOS, APP Android, WEBADMIN, ANDROID)\n\n**Risposte di successo:**\n- **200**: Default Response\n\n**Errori documentati:** 400, 401, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Auth.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["refresh_token"],"properties":{"refresh_token":{"type":"string"},"devicetype":{"type":"string","enum":["WEB","APP","APP iOS","APP Android","WEBADMIN","ANDROID","AI_CONNECTOR"],"description":"Opzionale: canale per il nuovo token (come in POST /oauth)"}}}}},"required":true},"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"access_token":{"type":"string"},"token_type":{"type":"string","example":"Bearer"},"expires_in":{"type":"number"},"refresh_token":{"type":"string","description":"Nuovo refresh token (il precedente è stato revocato)"},"refresh_expires_at":{"type":"string","format":"date-time"},"user":{"type":"object","properties":{"id":{"type":"integer"},"username":{"type":"string"},"email":{"type":"string","nullable":true},"firstName":{"type":"string","nullable":true},"lastName":{"type":"string","nullable":true},"customerToken":{"type":"string","nullable":true},"programId":{"type":"integer","nullable":true},"programIds":{"type":"array","nullable":true,"items":{"type":"integer"},"description":"Programas que el operador puede abrir. null = todos (isSystem)."},"programName":{"type":"string","nullable":true},"isSystem":{"type":"boolean"},"roles":{"type":"array","items":{"type":"string"}},"deviceType":{"type":"integer","description":"1=WEB, 2=APP iOS, 3=APP Android, 4=WEBADMIN, 5=ANDROID","nullable":true},"mustChangePassword":{"type":"boolean"},"language":{"type":"string","enum":["it","en","es"]}}}},"example":{"access_token":"","token_type":"Bearer","expires_in":0,"refresh_token":"","refresh_expires_at":"","user":{"id":0,"username":"","email":"","firstName":"","lastName":"","customerToken":"","programId":0,"programIds":[0],"programName":"","isSystem":false,"roles":[""],"deviceType":0,"mustChangePassword":false,"language":"it"}}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"Refresh token non valido o scaduto. Codice: `INVALID_REFRESH_TOKEN`.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}}}},"description":"Refresh token non valido o scaduto. Codice: `INVALID_REFRESH_TOKEN`.","example":{"error":{"code":"INVALID_REFRESH_TOKEN","message":"Invalid or expired refresh token","language":"it","messages":{"it":"Invalid or expired refresh token","en":"Invalid or expired refresh token","es":"Invalid or expired refresh token"},"path":"/oauth/refresh","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Invalid or expired refresh token"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/oauth/token":{"post":{"tags":["Auth"],"description":"Rinnova il token JWT tramite refresh token.\n\n**Contesto:** Autenticazione OAuth: scambio credenziali (operatore, cliente o terminale POS) con API Key e rilascio JWT (access + refresh token).\n\n**Autenticazione:** endpoint pubblico — non richiede `Authorization: Bearer` (può richiedere `X-API-Key` e/o OTP a seconda del caso).\n\n**Body JSON:**\n- `username` (string) — obbligatorio\n- `password` (string) — obbligatorio\n- `devicetype` (string) — opzionale: Canale: WEB, APP iOS, APP Android, WEBADMIN o ANDROID (salvato nel JWT e usato per filtri promo e movimenti) (valori: WEB, APP iOS, APP Android, WEBADMIN, ANDROID)\n- `api_key` (string) — opzionale: API Key alternativa a X-API-Key (opzionale)\n\n**Risposte di successo:**\n- **200**: Token (alias refresh)\n\n**Errori documentati:** 400, 401, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Auth.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["username","password"],"properties":{"username":{"type":"string","example":"operator1"},"password":{"type":"string","format":"password"},"devicetype":{"type":"string","enum":["WEB","APP","APP iOS","APP Android","WEBADMIN","ANDROID","AI_CONNECTOR"],"description":"Canale: WEB, APP, APP iOS, APP Android, WEBADMIN o ANDROID (salvato nel JWT e usato per filtri promo e movimenti)"},"api_key":{"type":"string","description":"API Key alternativa a X-API-Key (opzionale)"}}}}},"required":true},"parameters":[{"schema":{"type":"string"},"in":"header","name":"x-api-key","required":false,"description":"API Key (optional, can also be provided as api_key in body)"}],"responses":{"200":{"description":"Token (alias refresh)","content":{"application/json":{"schema":{"type":"object","properties":{"access_token":{"type":"string"},"token_type":{"type":"string","example":"Bearer"},"expires_in":{"type":"number"},"refresh_token":{"type":"string","nullable":true},"refresh_expires_at":{"type":"string","format":"date-time","nullable":true},"user":{"type":"object","properties":{"id":{"type":"integer"},"username":{"type":"string"},"email":{"type":"string","nullable":true},"firstName":{"type":"string","nullable":true},"lastName":{"type":"string","nullable":true},"customerToken":{"type":"string","nullable":true},"programId":{"type":"integer","nullable":true},"programIds":{"type":"array","nullable":true,"items":{"type":"integer"},"description":"Programas que el operador puede abrir. null = todos (isSystem)."},"programName":{"type":"string","nullable":true},"isSystem":{"type":"boolean"},"roles":{"type":"array","items":{"type":"string"}},"deviceType":{"type":"integer","description":"1=WEB, 2=APP iOS, 3=APP Android, 4=WEBADMIN, 5=ANDROID","nullable":true},"mustChangePassword":{"type":"boolean"},"language":{"type":"string","enum":["it","en","es"]}}}},"description":"Token (alias refresh)","example":{"access_token":"eyJ…","token_type":"Bearer","expires_in":600},"additionalProperties":true}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"Refresh token non valido o scaduto. Codice: `INVALID_REFRESH_TOKEN`.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}}}},"description":"Refresh token non valido o scaduto. Codice: `INVALID_REFRESH_TOKEN`.","example":{"error":{"code":"INVALID_REFRESH_TOKEN","message":"Invalid or expired refresh token","language":"it","messages":{"it":"Invalid or expired refresh token","en":"Invalid or expired refresh token","es":"Invalid or expired refresh token"},"path":"/oauth/refresh","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Invalid or expired refresh token"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/oauth/app/register":{"post":{"tags":["Auth"],"description":"Registra un account app generica (telefono + password) e invia l'OTP SMS. Se il numero è già in stato PENDING, aggiorna la password e reinvia l'OTP. Richiede APP_OTP_SMS_PROGRAM_ID.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["phone","password"],"properties":{"phone":{"type":"string","example":"+393331234567"},"password":{"type":"string","format":"password","minLength":8,"maxLength":72}}}}},"required":true},"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"example":{"ok":false}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}}}},"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"409":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}}},"422":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}}},"429":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/oauth/app/resend":{"post":{"tags":["Auth"],"description":"Reinvia l'OTP di registrazione se esiste un account PENDING. Se il numero non è in attesa di verifica risponde 200 senza inviare SMS. Se gli invii sono troppi, 429 OTP_RATE_LIMITED.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["phone"],"properties":{"phone":{"type":"string"}}}}},"required":true},"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"example":{"ok":false}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}}}},"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"422":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}}},"429":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/oauth/app/verify":{"post":{"tags":["Auth"],"description":"Conferma l'OTP di registrazione, attiva l'account e restituisce la sessione app (programs vuoto finché non si scansiona un QR).","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["phone","otp"],"properties":{"phone":{"type":"string"},"otp":{"type":"string","minLength":6,"maxLength":6},"devicetype":{"type":"string","enum":["WEB","APP","APP iOS","APP Android","WEBADMIN","ANDROID","AI_CONNECTOR"],"description":"Canale: WEB, APP, APP iOS, APP Android, WEBADMIN o ANDROID"}}}}},"required":true},"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"access_token":{"type":"string"},"token_type":{"type":"string","example":"Bearer"},"expires_in":{"type":"number"},"refresh_token":{"type":"string"},"refresh_expires_at":{"type":"string","format":"date-time"},"user":{"type":"object","properties":{"id":{"type":"integer"},"phone":{"type":"string"},"operatorType":{"type":"string","example":"APP"},"deviceType":{"type":"integer","nullable":true}}},"programs":{"type":"array","items":{"type":"object","properties":{"programId":{"type":"integer"},"name":{"type":"string","nullable":true},"logoPath":{"type":"string","nullable":true},"urlImages":{"type":"string","nullable":true},"appParameters":{"type":"object","nullable":true,"additionalProperties":true},"customerId":{"type":"integer"},"firstName":{"type":"string","nullable":true},"lastName":{"type":"string","nullable":true},"customerToken":{"type":"string","nullable":true},"cardNumber":{"type":"string","nullable":true},"shopId":{"type":"integer","nullable":true},"shopName":{"type":"string","nullable":true},"linkedAt":{"type":"string","nullable":true}}}}},"example":{"access_token":"","token_type":"Bearer","expires_in":0,"refresh_token":"","refresh_expires_at":"","user":{"id":0,"phone":"","operatorType":"APP","deviceType":0},"programs":[{"programId":0,"name":"","logoPath":"","urlImages":"","appParameters":null,"customerId":0,"firstName":"","lastName":"","customerToken":"","cardNumber":"","shopId":0,"shopName":"","linkedAt":""}]}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/oauth/app/login":{"post":{"tags":["Auth"],"description":"Login app generica. Telefono sconosciuto, password errata e account bloccato rispondono tutti 401 INVALID_CREDENTIALS. La lista programs contiene solo i programmi collegati con il QR.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["phone","password"],"properties":{"phone":{"type":"string"},"password":{"type":"string","format":"password"},"devicetype":{"type":"string","enum":["WEB","APP","APP iOS","APP Android","WEBADMIN","ANDROID","AI_CONNECTOR"],"description":"Canale: WEB, APP, APP iOS, APP Android, WEBADMIN o ANDROID"}}}}},"required":true},"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"access_token":{"type":"string"},"token_type":{"type":"string","example":"Bearer"},"expires_in":{"type":"number"},"refresh_token":{"type":"string"},"refresh_expires_at":{"type":"string","format":"date-time"},"user":{"type":"object","properties":{"id":{"type":"integer"},"phone":{"type":"string"},"operatorType":{"type":"string","example":"APP"},"deviceType":{"type":"integer","nullable":true}}},"programs":{"type":"array","items":{"type":"object","properties":{"programId":{"type":"integer"},"name":{"type":"string","nullable":true},"logoPath":{"type":"string","nullable":true},"urlImages":{"type":"string","nullable":true},"appParameters":{"type":"object","nullable":true,"additionalProperties":true},"customerId":{"type":"integer"},"firstName":{"type":"string","nullable":true},"lastName":{"type":"string","nullable":true},"customerToken":{"type":"string","nullable":true},"cardNumber":{"type":"string","nullable":true},"shopId":{"type":"integer","nullable":true},"shopName":{"type":"string","nullable":true},"linkedAt":{"type":"string","nullable":true}}}}},"example":{"access_token":"","token_type":"Bearer","expires_in":0,"refresh_token":"","refresh_expires_at":"","user":{"id":0,"phone":"","operatorType":"APP","deviceType":0},"programs":[{"programId":0,"name":"","logoPath":"","urlImages":"","appParameters":null,"customerId":0,"firstName":"","lastName":"","customerToken":"","cardNumber":"","shopId":0,"shopName":"","linkedAt":""}]}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/oauth/app/refresh":{"post":{"tags":["Auth"],"description":"Ruota il refresh token dell'app generica e restituisce una nuova sessione con la lista programmi.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["refresh_token"],"properties":{"refresh_token":{"type":"string"}}}}},"required":true},"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"access_token":{"type":"string"},"token_type":{"type":"string","example":"Bearer"},"expires_in":{"type":"number"},"refresh_token":{"type":"string"},"refresh_expires_at":{"type":"string","format":"date-time"},"user":{"type":"object","properties":{"id":{"type":"integer"},"phone":{"type":"string"},"operatorType":{"type":"string","example":"APP"},"deviceType":{"type":"integer","nullable":true}}},"programs":{"type":"array","items":{"type":"object","properties":{"programId":{"type":"integer"},"name":{"type":"string","nullable":true},"logoPath":{"type":"string","nullable":true},"urlImages":{"type":"string","nullable":true},"appParameters":{"type":"object","nullable":true,"additionalProperties":true},"customerId":{"type":"integer"},"firstName":{"type":"string","nullable":true},"lastName":{"type":"string","nullable":true},"customerToken":{"type":"string","nullable":true},"cardNumber":{"type":"string","nullable":true},"shopId":{"type":"integer","nullable":true},"shopName":{"type":"string","nullable":true},"linkedAt":{"type":"string","nullable":true}}}}},"example":{"access_token":"","token_type":"Bearer","expires_in":0,"refresh_token":"","refresh_expires_at":"","user":{"id":0,"phone":"","operatorType":"APP","deviceType":0},"programs":[{"programId":0,"name":"","logoPath":"","urlImages":"","appParameters":null,"customerId":0,"firstName":"","lastName":"","customerToken":"","cardNumber":"","shopId":0,"shopName":"","linkedAt":""}]}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/oauth/app/forgot":{"post":{"tags":["Auth"],"description":"Invia un OTP di reset password se il telefono ha un account ACTIVE. Risponde sempre 200, anche se il numero non esiste.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["phone"],"properties":{"phone":{"type":"string"}}}}},"required":true},"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"example":{"ok":false}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/oauth/app/reset":{"post":{"tags":["Auth"],"description":"Imposta una nuova password con l'OTP di reset e restituisce la sessione app.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["phone","otp","password"],"properties":{"phone":{"type":"string"},"otp":{"type":"string"},"password":{"type":"string","format":"password","minLength":8,"maxLength":72},"devicetype":{"type":"string","enum":["WEB","APP","APP iOS","APP Android","WEBADMIN","ANDROID","AI_CONNECTOR"],"description":"Canale: WEB, APP, APP iOS, APP Android, WEBADMIN o ANDROID"}}}}},"required":true},"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"access_token":{"type":"string"},"token_type":{"type":"string","example":"Bearer"},"expires_in":{"type":"number"},"refresh_token":{"type":"string"},"refresh_expires_at":{"type":"string","format":"date-time"},"user":{"type":"object","properties":{"id":{"type":"integer"},"phone":{"type":"string"},"operatorType":{"type":"string","example":"APP"},"deviceType":{"type":"integer","nullable":true}}},"programs":{"type":"array","items":{"type":"object","properties":{"programId":{"type":"integer"},"name":{"type":"string","nullable":true},"logoPath":{"type":"string","nullable":true},"urlImages":{"type":"string","nullable":true},"appParameters":{"type":"object","nullable":true,"additionalProperties":true},"customerId":{"type":"integer"},"firstName":{"type":"string","nullable":true},"lastName":{"type":"string","nullable":true},"customerToken":{"type":"string","nullable":true},"cardNumber":{"type":"string","nullable":true},"shopId":{"type":"integer","nullable":true},"shopName":{"type":"string","nullable":true},"linkedAt":{"type":"string","nullable":true}}}}},"example":{"access_token":"","token_type":"Bearer","expires_in":0,"refresh_token":"","refresh_expires_at":"","user":{"id":0,"phone":"","operatorType":"APP","deviceType":0},"programs":[{"programId":0,"name":"","logoPath":"","urlImages":"","appParameters":null,"customerId":0,"firstName":"","lastName":"","customerToken":"","cardNumber":"","shopId":0,"shopName":"","linkedAt":""}]}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/app/programs":{"get":{"tags":["App"],"description":"Programmi dell'account app. Include i clienti già registrati con lo stesso telefono nei programmi con app generica attiva, più quelli aggiunti col QR. Bearer JWT operatorType=APP.","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"programs":{"type":"array","items":{"type":"object","properties":{"programId":{"type":"integer"},"name":{"type":"string","nullable":true},"logoPath":{"type":"string","nullable":true},"urlImages":{"type":"string","nullable":true},"appParameters":{"type":"object","nullable":true,"additionalProperties":true},"customerId":{"type":"integer"},"firstName":{"type":"string","nullable":true},"lastName":{"type":"string","nullable":true},"customerToken":{"type":"string","nullable":true},"cardNumber":{"type":"string","nullable":true},"shopId":{"type":"integer","nullable":true},"shopName":{"type":"string","nullable":true},"linkedAt":{"type":"string","nullable":true}}}}},"example":{"programs":[{"programId":0,"name":"","logoPath":"","urlImages":"","appParameters":null,"customerId":0,"firstName":"","lastName":"","customerToken":"","cardNumber":"","shopId":0,"shopName":"","linkedAt":""}]}}}}},"401":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}}},"403":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"post":{"tags":["App"],"description":"Collega un programma scansionando il token del QR di negozio (ae1.*). Bearer JWT operatorType=APP. Se il programma è già collegato, 200 con created=false.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["token"],"properties":{"token":{"type":"string","description":"Contenuto del QR (ae1.<payload>)"}}}}},"required":true},"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"program":{"type":"object","properties":{"programId":{"type":"integer"},"name":{"type":"string","nullable":true},"logoPath":{"type":"string","nullable":true},"urlImages":{"type":"string","nullable":true},"appParameters":{"type":"object","nullable":true,"additionalProperties":true},"customerId":{"type":"integer"},"firstName":{"type":"string","nullable":true},"lastName":{"type":"string","nullable":true},"customerToken":{"type":"string","nullable":true},"cardNumber":{"type":"string","nullable":true},"shopId":{"type":"integer","nullable":true},"shopName":{"type":"string","nullable":true},"linkedAt":{"type":"string","nullable":true}}},"created":{"type":"boolean"}},"example":{"program":{"programId":0,"name":"","logoPath":"","urlImages":"","appParameters":null,"customerId":0,"firstName":"","lastName":"","customerToken":"","cardNumber":"","shopId":0,"shopName":"","linkedAt":""},"created":false}}}}},"201":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"program":{"type":"object","properties":{"programId":{"type":"integer"},"name":{"type":"string","nullable":true},"logoPath":{"type":"string","nullable":true},"urlImages":{"type":"string","nullable":true},"appParameters":{"type":"object","nullable":true,"additionalProperties":true},"customerId":{"type":"integer"},"firstName":{"type":"string","nullable":true},"lastName":{"type":"string","nullable":true},"customerToken":{"type":"string","nullable":true},"cardNumber":{"type":"string","nullable":true},"shopId":{"type":"integer","nullable":true},"shopName":{"type":"string","nullable":true},"linkedAt":{"type":"string","nullable":true}}},"created":{"type":"boolean"}},"example":{"program":{"programId":0,"name":"","logoPath":"","urlImages":"","appParameters":null,"customerId":0,"firstName":"","lastName":"","customerToken":"","cardNumber":"","shopId":0,"shopName":"","linkedAt":""},"created":false}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}}}},"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"403":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}}},"404":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}}},"409":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/app/programs/{programId}/session":{"post":{"tags":["App"],"description":"Scambia la sessione app con un JWT operatorType=CUSTOMER del programma collegato. Da quel momento valgono le API dell'area cliente (saldi, coupon, movimenti, negozi). Il refresh_token restituito si rinnova con POST /oauth/refresh.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"devicetype":{"type":"string","enum":["WEB","APP","APP iOS","APP Android","WEBADMIN","ANDROID","AI_CONNECTOR"],"description":"Canale: WEB, APP, APP iOS, APP Android, WEBADMIN o ANDROID"}}}}}},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"access_token":{"type":"string"},"token_type":{"type":"string"},"expires_in":{"type":"number"},"refresh_token":{"type":"string"},"refresh_expires_at":{"type":"string"},"user":{"type":"object","properties":{"id":{"type":"integer"},"username":{"type":"string","nullable":true},"email":{"type":"string","nullable":true},"firstName":{"type":"string","nullable":true},"lastName":{"type":"string","nullable":true},"customerToken":{"type":"string","nullable":true},"programId":{"type":"integer"},"operatorType":{"type":"string","example":"CUSTOMER"},"deviceType":{"type":"integer","nullable":true}}}},"example":{"access_token":"","token_type":"","expires_in":0,"refresh_token":"","refresh_expires_at":"","user":{"id":0,"username":"","email":"","firstName":"","lastName":"","customerToken":"","programId":0,"operatorType":"CUSTOMER","deviceType":0}}}}}},"401":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}}},"403":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}}},"404":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/shops/{shopId}/app-enrollment-token":{"get":{"tags":["Shops"],"description":"Token cifrato da mettere nel QR di negozio dell'app generica. Il QR contiene solo la stringa token (prefisso ae1.). Richiede JWT operatore con shops:read, MARTA_SECRET_KEY e businessRules.appGeneric.enabled.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"shopId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"token":{"type":"string"},"programId":{"type":"integer"},"shopId":{"type":"integer"}},"example":{"token":"","programId":0,"shopId":0}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}}}},"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}}}},"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}}}},"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}},"503":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}}}}}}}}}}},"/api/v1/programs":{"get":{"tags":["Programs"],"description":"Elenco paginato di programma fedeltà.\n\n**Contesto:** Un **Programma** è l'istanza isolata del loyalty (ex Campaign): contiene negozi, clienti, wallet, promo, premi e tutte le configurazioni. Livello SYSTEM per la gestione piattaforma.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Querystring:**\n- `page` (integer) — opzionale [default: 1]\n- `size` (integer) — opzionale [default: 20]\n- `sort` (string) — opzionale (valori: id, name, created_at, issued) [default: \"id\"]\n- `order` (string) — opzionale (valori: asc, desc) [default: \"asc\"]\n- `status` (string) — opzionale (valori: ENABLED, DISABLED)\n\n**Risposte di successo:**\n- **200**: Lista programmi paginata.\n\n**Errori documentati:** 400, 401, 403, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Programs.","parameters":[{"schema":{"type":"integer","minimum":1,"default":1},"in":"query","name":"page","required":false},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":20},"in":"query","name":"size","required":false},{"schema":{"type":"string","enum":["id","name","created_at","issued"],"default":"id"},"in":"query","name":"sort","required":false},{"schema":{"type":"string","enum":["asc","desc"],"default":"asc"},"in":"query","name":"order","required":false},{"schema":{"type":"string","enum":["ENABLED","DISABLED"]},"in":"query","name":"status","required":false}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Lista programmi paginata.","content":{"application/json":{"schema":{"description":"Lista programmi paginata.","type":"object","additionalProperties":true,"example":{"content":[{"id":1,"name":"Programma fedeltà demo","description":"Descrizione programma","logoPath":"/uploads/logos/p1.png","urlImages":"https://cdn.example.com/banner.jpg","localCurrencyId":1,"timezoneId":1,"status":"ENABLED","defaultPhonePrefix":"+39","autoChargeWalletIds":[1,2],"appParameters":"{\"deeplinkBase\":\"https://app.example\"}","businessRules":{"familyCards":{"enabled":false,"onlyFatherUse":true},"movements":{"cancelWithinMinutes":0},"shopWallet":{"enabled":false},"shopWeightings":{"shopsCanChangeWeighting":false,"enforceContractualFloor":true}},"donationSettings":{"enabled":false,"maxProjectsPerCustomer":3,"distributionMode":"HOMOGENEOUS"},"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}],"totalElements":1,"totalPages":1,"page":1,"size":20}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"post":{"tags":["Programs"],"description":"Crea programma fedeltà.\n\n**Contesto:** Un **Programma** è l'istanza isolata del loyalty (ex Campaign): contiene negozi, clienti, wallet, promo, premi e tutte le configurazioni. Livello SYSTEM per la gestione piattaforma.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Body JSON:**\n- `name` (string) — obbligatorio\n- `description` (string) — opzionale\n- `logo_path` (string) — opzionale\n- `url_images` (string) — opzionale\n- `local_currency_id` (integer) — opzionale\n- `timezone_id` (integer) — opzionale\n- `defaultPhonePrefix` (string) — opzionale: Prefisso telefonico internazionale (es. +39)\n- `appParameters` (string) — opzionale: Parametri APP (JSON testuale o oggetto, max 4000 caratteri)\n- `businessRules` (string) — opzionale: Regole business (JSON, es. familyCards)\n\n**Risposte di successo:**\n- **201**: Programma creato.\n\n**Errori documentati:** 400, 401, 403, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Programs.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["name"],"properties":{"name":{"type":"string","minLength":1},"description":{"type":"string"},"logo_path":{"type":"string"},"url_images":{"type":"string"},"local_currency_id":{"type":"integer","nullable":true},"timezone_id":{"type":"integer","nullable":true},"defaultPhonePrefix":{"type":"string","description":"Prefisso telefonico internazionale (es. +39)","nullable":true},"appParameters":{"anyOf":[{"type":"string"},{"type":"object","additionalProperties":true},{"type":"null"}],"description":"Parametri APP (JSON testuale o oggetto, max 4000 caratteri)"},"businessRules":{"anyOf":[{"type":"string"},{"type":"object","additionalProperties":true},{"type":"null"}],"description":"Regole business (JSON, es. familyCards)"}}}}},"required":true},"security":[{"bearerAuth":[]}],"responses":{"201":{"description":"Programma creato.","content":{"application/json":{"schema":{"description":"Programma creato.","type":"object","additionalProperties":true,"example":{"id":1,"name":"Programma fedeltà demo","description":"Descrizione programma","logoPath":"/uploads/logos/p1.png","urlImages":"https://cdn.example.com/banner.jpg","localCurrencyId":1,"timezoneId":1,"status":"ENABLED","defaultPhonePrefix":"+39","autoChargeWalletIds":[1,2],"appParameters":"{\"deeplinkBase\":\"https://app.example\"}","businessRules":{"familyCards":{"enabled":false,"onlyFatherUse":true},"movements":{"cancelWithinMinutes":0},"shopWallet":{"enabled":false},"shopWeightings":{"shopsCanChangeWeighting":false,"enforceContractualFloor":true}},"donationSettings":{"enabled":false,"maxProjectsPerCustomer":3,"distributionMode":"HOMOGENEOUS"},"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{id}":{"get":{"tags":["Programs"],"description":"Dettaglio di una programma fedeltà per identificativo del programma indicato in `programId`.\n\n**Contesto:** Un **Programma** è l'istanza isolata del loyalty (ex Campaign): contiene negozi, clienti, wallet, promo, premi e tutte le configurazioni. Livello SYSTEM per la gestione piattaforma.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `id` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Dettaglio programma (camelCase).\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Programs.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Dettaglio programma (camelCase).","content":{"application/json":{"schema":{"description":"Dettaglio programma (camelCase).","type":"object","additionalProperties":true,"example":{"id":1,"name":"Programma fedeltà demo","description":"Descrizione programma","logoPath":"/uploads/logos/p1.png","urlImages":"https://cdn.example.com/banner.jpg","localCurrencyId":1,"timezoneId":1,"status":"ENABLED","defaultPhonePrefix":"+39","autoChargeWalletIds":[1,2],"appParameters":"{\"deeplinkBase\":\"https://app.example\"}","businessRules":{"familyCards":{"enabled":false,"onlyFatherUse":true},"movements":{"cancelWithinMinutes":0},"shopWallet":{"enabled":false},"shopWeightings":{"shopsCanChangeWeighting":false,"enforceContractualFloor":true}},"donationSettings":{"enabled":false,"maxProjectsPerCustomer":3,"distributionMode":"HOMOGENEOUS"},"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"put":{"tags":["Programs"],"description":"Aggiorna una programma fedeltà (campi consentiti) del programma indicato in `programId`.\n\n**Contesto:** Un **Programma** è l'istanza isolata del loyalty (ex Campaign): contiene negozi, clienti, wallet, promo, premi e tutte le configurazioni. Livello SYSTEM per la gestione piattaforma.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `id` (integer) — obbligatorio\n\n**Body JSON:**\n- `name` (string) — opzionale\n- `description` (string) — opzionale\n- `logo_path` (string) — opzionale\n- `url_images` (string) — opzionale\n- `local_currency_id` (integer) — opzionale\n- `localCurrencyId` (integer) — opzionale\n- `timezone_id` (integer) — opzionale\n- `timezoneId` (integer) — opzionale\n- `defaultPhonePrefix` (string) — opzionale: Prefisso telefonico internazionale (es. +39)\n- `autoChargeWalletIds` (array) — opzionale: Wallet id da caricare in automatico a ogni spesa\n- `status` (string) — opzionale (valori: ENABLED, DISABLED)\n- `app_parameters` (string) — opzionale: Parametri APP (JSON testuale, max 4000 caratteri)\n- `appParameters` (string) — opzionale: Parametri APP (JSON testuale o oggetto, max 4000 caratteri)\n- `business_rules` (string) — opzionale: Regole business (JSON)\n- `businessRules` (string) — opzionale: Regole business (JSON)\n\n**Risposte di successo:**\n- **200**: Programma aggiornato.\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Programs.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"description":{"type":"string"},"logo_path":{"type":"string","nullable":true},"logoPath":{"type":"string","nullable":true},"url_images":{"type":"string","nullable":true},"urlImages":{"type":"string","nullable":true},"local_currency_id":{"type":"integer","nullable":true},"localCurrencyId":{"type":"integer","nullable":true},"timezone_id":{"type":"integer","nullable":true},"timezoneId":{"type":"integer","nullable":true},"defaultPhonePrefix":{"type":"string","description":"Prefisso telefonico internazionale (es. +39)","nullable":true},"autoChargeWalletIds":{"type":"array","items":{"type":"integer"},"description":"Wallet id da caricare in automatico a ogni spesa"},"status":{"type":"string","enum":["ENABLED","DISABLED"]},"app_parameters":{"anyOf":[{"type":"string"},{"type":"object","additionalProperties":true},{"type":"null"}],"description":"Parametri APP (JSON testuale, max 4000 caratteri)"},"appParameters":{"anyOf":[{"type":"string"},{"type":"object","additionalProperties":true},{"type":"null"}],"description":"Parametri APP (JSON testuale o oggetto, max 4000 caratteri)"},"business_rules":{"anyOf":[{"type":"string"},{"type":"object","additionalProperties":true},{"type":"null"}],"description":"Regole business (JSON)"},"businessRules":{"anyOf":[{"type":"string"},{"type":"object","additionalProperties":true},{"type":"null"}],"description":"Regole business (JSON)"},"tts_settings":{"type":"object","additionalProperties":false,"properties":{"provider":{"type":"string","enum":["polly","openai"]},"voice":{"type":"string"},"openaiVoice":{"type":"string"},"pollyVoices":{"type":"object","additionalProperties":false,"properties":{"it":{"type":"string"},"es":{"type":"string"},"en":{"type":"string"}}},"rate":{"type":"number","minimum":0.75,"maximum":1.5}},"required":["provider"]},"ttsSettings":{"type":"object","additionalProperties":false,"properties":{"provider":{"type":"string","enum":["polly","openai"]},"voice":{"type":"string"},"openaiVoice":{"type":"string"},"pollyVoices":{"type":"object","additionalProperties":false,"properties":{"it":{"type":"string"},"es":{"type":"string"},"en":{"type":"string"}}},"rate":{"type":"number","minimum":0.75,"maximum":1.5}},"required":["provider"]}}}}}},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Programma aggiornato.","content":{"application/json":{"schema":{"description":"Programma aggiornato.","type":"object","additionalProperties":true,"example":{"id":1,"name":"Programma fedeltà demo","description":"Descrizione programma","logoPath":"/uploads/logos/p1.png","urlImages":"https://cdn.example.com/banner.jpg","localCurrencyId":1,"timezoneId":1,"status":"ENABLED","defaultPhonePrefix":"+39","autoChargeWalletIds":[1,2],"appParameters":"{\"deeplinkBase\":\"https://app.example\"}","businessRules":{"familyCards":{"enabled":false,"onlyFatherUse":true},"movements":{"cancelWithinMinutes":0},"shopWallet":{"enabled":false},"shopWeightings":{"shopsCanChangeWeighting":false,"enforceContractualFloor":true}},"donationSettings":{"enabled":false,"maxProjectsPerCustomer":3,"distributionMode":"HOMOGENEOUS"},"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"delete":{"tags":["Programs"],"description":"Elimina una programma fedeltà del programma indicato in `programId` (soft delete o `TERMINATED` se vincoli FK).\n\n**Contesto:** Un **Programma** è l'istanza isolata del loyalty (ex Campaign): contiene negozi, clienti, wallet, promo, premi e tutte le configurazioni. Livello SYSTEM per la gestione piattaforma.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `id` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **204**: Eliminazione riuscita (nessun corpo).\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Programs.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"204":{"description":"Eliminazione riuscita (nessun corpo)."},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{id}/anagrafica":{"get":{"tags":["Programs"],"description":"Campi anagrafici del cliente e flag obbligatorio, validi per ogni creazione cliente. Inclusi anche in GET /programs/:id (campo anagrafica).","parameters":[{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"put":{"tags":["Programs"],"description":"Aggiorna quali campi anagrafici sono obbligatori. Il cellulare, se obbligatorio, in creazione POS richiede anche l'OTP.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["fields"],"properties":{"fields":{"type":"array","items":{"type":"object","required":["key","mandatory"],"properties":{"key":{"type":"string"},"mandatory":{"type":"boolean"}}}}}}}},"required":true},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{id}/snapshot":{"get":{"tags":["Programs"],"description":"Compact program snapshot for AI agent: details, businessRules, wallets, tiers, promos, dashboard KPIs","parameters":[{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/nets":{"get":{"tags":["Nets"],"description":"Elenco paginato di rete del programma indicato in `programId`.\n\n**Contesto:** Le **Reti** organizzano i negozi in gerarchia (rete → sotto-rete). Ogni negozio può appartenere a una rete; le reti ereditano timezone e regole dal programma o dal padre.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n\n**Querystring:**\n- `parentId` (integer) — opzionale\n- `page` (integer) — opzionale [default: 1]\n- `size` (integer) — opzionale [default: 20]\n- `name` (string) — opzionale: Filtra per nome (ILIKE)\n- `description` (string) — opzionale: Filtra per descrizione (ILIKE)\n- `foreignId` (string) — opzionale: Filtra per foreignId (ILIKE)\n\n**Risposte di successo:**\n- **200**: Lista nets\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Nets.","parameters":[{"schema":{"type":"integer","nullable":true},"in":"query","name":"parentId","required":false},{"schema":{"type":"integer","default":1},"in":"query","name":"page","required":false},{"schema":{"type":"integer","default":20},"in":"query","name":"size","required":false},{"schema":{"type":"string"},"in":"query","name":"name","required":false,"description":"Filtra per nome (ILIKE)"},{"schema":{"type":"string"},"in":"query","name":"description","required":false,"description":"Filtra per descrizione (ILIKE)"},{"schema":{"type":"string"},"in":"query","name":"foreignId","required":false,"description":"Filtra per foreignId (ILIKE)"},{"schema":{"type":"integer"},"in":"path","name":"programId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Lista nets","content":{"application/json":{"schema":{"description":"Lista nets","type":"object","additionalProperties":true,"example":{"content":[{"id":1,"name":"Rete nord","description":null,"foreignId":null,"programId":1,"parentId":null,"shopsCount":2,"status":"ENABLED","createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}],"totalElements":1,"totalPages":1,"page":1,"size":20}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"post":{"tags":["Nets"],"description":"Crea rete del programma indicato in `programId`.\n\n**Contesto:** Le **Reti** organizzano i negozi in gerarchia (rete → sotto-rete). Ogni negozio può appartenere a una rete; le reti ereditano timezone e regole dal programma o dal padre.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n\n**Body JSON:**\n- `name` (string) — obbligatorio\n- `description` (string) — opzionale\n- `companyName` (string) — opzionale: Ignored in MARTA 2.0 (no column)\n- `parentId` (integer) — opzionale\n- `foreignId` (string) — opzionale\n- `timezoneId` (integer) — opzionale: Fuso orario rete (id da GET /api/v1/time-zones). NULL = eredita da rete padre o programma.\n- `timezone_id` (integer) — opzionale: Alias per timezoneId\n- `status` (string) — opzionale (valori: ENABLED, DISABLED)\n\n**Risposte di successo:**\n- **201**: Risorsa creata (nets)\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Nets.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["name"],"properties":{"name":{"type":"string"},"description":{"type":"string"},"companyName":{"type":"string","description":"Ignored in MARTA 2.0 (no column)"},"parentId":{"type":"integer","nullable":true},"foreignId":{"type":"string","nullable":true},"timezoneId":{"type":"integer","description":"Fuso orario rete (id da GET /api/v1/time-zones). NULL = eredita da rete padre o programma.","nullable":true},"timezone_id":{"type":"integer","description":"Alias per timezoneId","nullable":true},"status":{"type":"string","enum":["ENABLED","DISABLED"]}}}}},"required":true},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"201":{"description":"Risorsa creata (nets)","content":{"application/json":{"schema":{"description":"Risorsa creata (nets)","type":"object","additionalProperties":true,"example":{"id":1,"name":"Rete nord","description":null,"foreignId":null,"programId":1,"parentId":null,"shopsCount":2,"status":"ENABLED","createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/nets/{id}":{"get":{"tags":["Nets"],"description":"Dettaglio di una programma fedeltà per identificativo del programma indicato in `programId`.\n\n**Contesto:** Le **Reti** organizzano i negozi in gerarchia (rete → sotto-rete). Ogni negozio può appartenere a una rete; le reti ereditano timezone e regole dal programma o dal padre.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n- `id` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Dettaglio nets\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Nets.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Dettaglio nets","content":{"application/json":{"schema":{"description":"Dettaglio nets","type":"object","additionalProperties":true,"example":{"id":1,"name":"Rete nord","description":null,"foreignId":null,"programId":1,"parentId":null,"shopsCount":2,"status":"ENABLED","createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"put":{"tags":["Nets"],"description":"Aggiorna una programma fedeltà (campi consentiti) del programma indicato in `programId`.\n\n**Contesto:** Le **Reti** organizzano i negozi in gerarchia (rete → sotto-rete). Ogni negozio può appartenere a una rete; le reti ereditano timezone e regole dal programma o dal padre.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n- `id` (integer) — obbligatorio\n\n**Body JSON:**\n- `name` (string) — opzionale\n- `description` (string) — opzionale\n- `parentId` (integer) — opzionale\n- `foreignId` (string) — opzionale\n- `timezoneId` (integer) — opzionale: Fuso orario rete. NULL = eredita da rete padre o programma.\n- `timezone_id` (integer) — opzionale: Alias per timezoneId\n- `status` (string) — opzionale (valori: ENABLED, DISABLED)\n\n**Risposte di successo:**\n- **200**: Aggiornamento nets\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Nets.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"description":{"type":"string"},"parentId":{"type":"integer","nullable":true},"foreignId":{"type":"string","nullable":true},"timezoneId":{"type":"integer","description":"Fuso orario rete. NULL = eredita da rete padre o programma.","nullable":true},"timezone_id":{"type":"integer","description":"Alias per timezoneId","nullable":true},"status":{"type":"string","enum":["ENABLED","DISABLED"]}}}}}},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Aggiornamento nets","content":{"application/json":{"schema":{"description":"Aggiornamento nets","type":"object","additionalProperties":true,"example":{"id":1,"name":"Rete nord","description":null,"foreignId":null,"programId":1,"parentId":null,"shopsCount":2,"status":"ENABLED","createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"delete":{"tags":["Nets"],"description":"Elimina una programma fedeltà del programma indicato in `programId` (soft delete o `TERMINATED` se vincoli FK).\n\n**Contesto:** Le **Reti** organizzano i negozi in gerarchia (rete → sotto-rete). Ogni negozio può appartenere a una rete; le reti ereditano timezone e regole dal programma o dal padre.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n- `id` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **204**: Operazione riuscita senza corpo di risposta.\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Nets.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"204":{"description":"Operazione riuscita senza corpo di risposta."},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/shops":{"get":{"tags":["Shops"],"description":"List shops of a program. Con token CUSTOMER, ogni elemento include `preferred` (true se il negozio è tra i preferiti del cliente autenticato).\n\n**Contesto:** I **Negozi** sono i punti vendita del programma (fisici o online). Hanno indirizzo geocodificato, orari, categorie, terminali e possono avere un borsellino negozio dedicato.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n\n**Querystring:**\n- `netId` (integer) — opzionale\n- `withoutNet` (boolean) — opzionale: Se true, restituisce solo negozi senza rete (net_id IS NULL)\n- `categoryId` (integer) — opzionale: Filtra per categoria shop (shop_categories.id)\n- `name` (string) — opzionale: Filtra per nome (ILIKE)\n- `foreignId` (string) — opzionale: Filtra per foreignId (ILIKE)\n- `companyName` (string) — opzionale: Filtra per ragione sociale (ILIKE)\n- `taxNumber` (string) — opzionale: Filtra per partita IVA / codice fiscale (ILIKE)\n- `city` (string) — opzionale: Filtra per città dell'indirizzo (ILIKE)\n- `state` (string) — opzionale: Filtra per provincia/stato dell'indirizzo (ILIKE)\n- `zipCode` (string) — opzionale: Filtra per CAP dell'indirizzo (match esatto)\n- `status` (string) — opzionale: Filtra per stato (ENABLED, DISABLED, TERMINATED) (valori: ENABLED, DISABLED, TERMINATED)\n- `kind` (string) — opzionale: Filtra per tipo negozio (OFFLINE, ONLINE) (valori: OFFLINE, ONLINE)\n- `benefit` (array) — opzionale: Filtro benefit tokenizzato (selezionabile): esempio [GC, AND, PR, OR, NS] [default: []]\n- `title` (string) — opzionale: Ricerca testuale: prima per nome negozio, poi GC, OC, PR, NS, EV (solo i benefit non esclusi dal filtro benefit)\n- `radius` (number) — opzionale: Raggio in metri (richiede geoLat e geoLong)\n- `geoLat` (number) — opzionale: Latitudine centro ricerca (WGS84), con radius\n- `geoLong` (number) — opzionale: Longitudine centro ricerca (WGS84), con radius\n- `sort` (string) — opzionale: Campo ordinamento (valori: id, name, foreignId) [default: \"name\"]\n- `order` (string) — opzionale: Direzione ordinamento (valori: asc, desc) [default: \"asc\"]\n- `page` (integer) — opzionale [default: 1]\n- `size` (integer) — opzionale [default: 20]\n\n**Risposte di successo:**\n- **200**: Lista shops\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Shops.","parameters":[{"schema":{"type":"integer"},"in":"query","name":"netId","required":false},{"schema":{"type":"boolean"},"in":"query","name":"withoutNet","required":false,"description":"Se true, restituisce solo negozi senza rete (net_id IS NULL)"},{"schema":{"type":"integer"},"in":"query","name":"categoryId","required":false,"description":"Filtra per categoria shop (shop_categories.id)"},{"schema":{"type":"string"},"in":"query","name":"name","required":false,"description":"Filtra per nome (ILIKE)"},{"schema":{"type":"string"},"in":"query","name":"foreignId","required":false,"description":"Filtra per foreignId (ILIKE)"},{"schema":{"type":"string"},"in":"query","name":"companyName","required":false,"description":"Filtra per ragione sociale (ILIKE)"},{"schema":{"type":"string"},"in":"query","name":"taxNumber","required":false,"description":"Filtra per partita IVA / codice fiscale (ILIKE)"},{"schema":{"type":"string"},"in":"query","name":"city","required":false,"description":"Filtra per città dell'indirizzo (ILIKE)"},{"schema":{"type":"string"},"in":"query","name":"state","required":false,"description":"Filtra per provincia/stato dell'indirizzo (ILIKE)"},{"schema":{"type":"string"},"in":"query","name":"zipCode","required":false,"description":"Filtra per CAP dell'indirizzo (match esatto)"},{"schema":{"type":"string","enum":["ENABLED","DISABLED","TERMINATED"]},"in":"query","name":"status","required":false,"description":"Filtra per stato (ENABLED, DISABLED, TERMINATED)"},{"schema":{"type":"string","enum":["OFFLINE","ONLINE"]},"in":"query","name":"kind","required":false,"description":"Filtra per tipo negozio (OFFLINE, ONLINE)"},{"schema":{"type":"array","items":{"type":"string","enum":["GC","OC","PR","NS","EV","AND","OR"]},"default":[]},"in":"query","name":"benefit","required":false,"description":"Filtro benefit tokenizzato (selezionabile): esempio [GC, AND, PR, OR, NS]"},{"schema":{"type":"string"},"in":"query","name":"title","required":false,"description":"Ricerca testuale: sul nome prima una sola parola che inizia col testo, poi i nomi più lunghi che iniziano così, poi una parola interna, poi chi lo contiene (prima una sola parola); a seguire GC, OC, PR, NS, EV (solo i benefit non esclusi dal filtro benefit)"},{"schema":{"type":"number"},"in":"query","name":"radius","required":false,"description":"Raggio in metri (richiede geoLat e geoLong)"},{"schema":{"type":"number"},"in":"query","name":"geoLat","required":false,"description":"Latitudine centro ricerca (WGS84), con radius"},{"schema":{"type":"number"},"in":"query","name":"geoLong","required":false,"description":"Longitudine centro ricerca (WGS84), con radius"},{"schema":{"type":"string","enum":["id","name","foreignId"],"default":"name"},"in":"query","name":"sort","required":false,"description":"Campo ordinamento"},{"schema":{"type":"string","enum":["asc","desc"],"default":"asc"},"in":"query","name":"order","required":false,"description":"Direzione ordinamento"},{"schema":{"type":"string","enum":["only","first","pin"]},"in":"query","name":"topShops","required":false,"description":"Campo dinamico shop \"Top Shop\". only = solo quelli attivi; first = quelli attivi per primi; pin = i primi 2, poi il resto. Se il campo non è configurato: only restituisce lista vuota, first e pin non cambiano l'ordine."},{"schema":{"type":"integer","default":1},"in":"query","name":"page","required":false},{"schema":{"type":"integer","default":20},"in":"query","name":"size","required":false},{"schema":{"type":"integer"},"in":"path","name":"programId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Lista shops","content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"properties":{"content":{"type":"array","items":{"type":"object","additionalProperties":true,"properties":{"id":{"type":"integer"},"name":{"type":"string"},"benefits":{"type":"object","additionalProperties":false,"properties":{"wallets":{"type":"array","items":{"type":"object","additionalProperties":true}},"giftCards":{"type":"array","items":{"type":"object","additionalProperties":true}},"coupons":{"type":"array","items":{"type":"object","additionalProperties":true}},"promotions":{"type":"array","items":{"type":"object","additionalProperties":true}},"news":{"type":"array","items":{"type":"object","additionalProperties":true}},"events":{"type":"array","items":{"type":"object","additionalProperties":true}}}}},"required":["id","name","benefits"]}},"totalElements":{"type":"integer"},"totalPages":{"type":"integer"},"page":{"type":"integer"},"size":{"type":"integer"},"countsByNet":{"type":"array","description":"Conteggi per rete con gli stessi filtri della lista (netId null = negozi senza rete).","items":{"type":"object","additionalProperties":false,"properties":{"netId":{"type":"integer","nullable":true},"count":{"type":"integer"}},"required":["netId","count"]}}},"required":["content","totalElements","totalPages","page","size","countsByNet"],"example":{"content":[{"id":12,"name":"Shop Demo","benefits":{"wallets":[{"walletId":1,"walletName":"Punti","walletSymbol":"PTS","chargeUnitsPerAmount":1,"chargeAmountPerUnit":1,"chargeCurrencyLabel":"€"}],"giftCards":[{"id":8,"name":"Gift Card 50"}],"coupons":[{"id":5,"title":"Offerta Primavera","mainImageUrl":"https://cdn.example/offer-main.jpg","publishFrom":"2026-05-01T00:00:00.000Z","publishTo":"2026-06-01T00:00:00.000Z","validFrom":"2026-05-01T00:00:00.000Z","validTo":"2026-06-15T23:59:59.000Z","variants":[{"description":"Coupon 10%","listPrice":100,"offerPrice":90,"discountPercent":10,"imageUrl":"https://cdn.example/offer-variant.jpg"}]}],"promotions":[{"id":9,"name":"Promo Spesa","validFrom":"2026-05-01T00:00:00.000Z","validTo":"2026-06-01T00:00:00.000Z","imageUrl":"https://cdn.example/promo.jpg","action":"WALLET"}],"news":[{"id":21,"title":"Nuova apertura","shortText":"Apre il nuovo punto vendita","validFrom":"2026-05-01T00:00:00.000Z","validTo":"2026-06-01T00:00:00.000Z","images":[{"id":3,"url":"https://cdn.example/news.jpg","sortOrder":0}]}],"events":[{"id":22,"title":"Evento degustazione","isEvent":true}]}}],"totalElements":1,"totalPages":1,"page":1,"size":20,"countsByNet":[{"netId":3,"count":1}]},"description":"Lista shops"}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"post":{"tags":["Shops"],"description":"Crea negozio del programma indicato in `programId`.\n\n**Contesto:** I **Negozi** sono i punti vendita del programma (fisici o online). Hanno indirizzo geocodificato, orari, categorie, terminali e possono avere un borsellino negozio dedicato.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n\n**Body JSON:**\n- `name` (string) — obbligatorio\n- `description` (string) — opzionale\n- `companyName` (string) — opzionale: Ragione sociale\n- `taxNumber` (string) — opzionale: Partita IVA / Codice Fiscale\n- `netId` (integer) — opzionale\n- `addressId` (integer) — opzionale\n- `phone` (string) — opzionale\n- `email` (string) — opzionale\n- `foreignId` (string) — opzionale\n- `createTerminal` (boolean) — opzionale: Se true crea automaticamente un terminale ENABLED nello shop appena creato. [default: false]\n- `localCurrencyId` (integer) — opzionale: Id moneta di default del negozio (override di program.localCurrencyId)\n- `timezoneId` (integer) — opzionale: Fuso orario negozio (id da GET /api/v1/time-zones). NULL = eredita da rete/sottorete o programma.\n- `timezone_id` (integer) — opzionale: Alias per timezoneId\n- `kind` (string) — opzionale: OFFLINE (default) o ONLINE (valori: OFFLINE, ONLINE)\n- `status` (string) — opzionale (valori: ENABLED, DISABLED)\n- `openingHours` (array) — opzionale: Configurazione orari: array di giorni con ranges [{start,end}] in formato HH:mm\n- `categoryIds` (array) — opzionale: ID categorie shop_categories\n- `categories` (array) — opzionale: Alias per categoryIds\n- `website` (string) — opzionale: Sito web negozio\n- `ecommerce` (string) — opzionale: URL e-commerce\n- `logo` (string) — opzionale: URL logo (es. da upload media shops)\n- `contactPerson` (string) — opzionale: Persona di contatto / referente\n- `dynamicFields` (array) — opzionale: Valori campi dinamici negozio (kind=SHOP)\n\n**Risposte di successo:**\n- **201**: Risorsa creata (shops)\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Shops.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["name"],"properties":{"name":{"type":"string"},"description":{"type":"string"},"companyName":{"type":"string","description":"Ragione sociale"},"taxNumber":{"type":"string","description":"Partita IVA / Codice Fiscale","nullable":true},"netId":{"type":"integer","nullable":true},"addressId":{"type":"integer","nullable":true},"phone":{"type":"string","nullable":true},"email":{"type":"string","format":"email","nullable":true},"foreignId":{"type":"string","nullable":true},"createTerminal":{"type":"boolean","default":false,"description":"Se true crea automaticamente un terminale ENABLED nello shop appena creato."},"localCurrencyId":{"type":"integer","description":"Id moneta di default del negozio (override di program.localCurrencyId)","nullable":true},"timezoneId":{"type":"integer","description":"Fuso orario negozio (id da GET /api/v1/time-zones). NULL = eredita da rete/sottorete o programma.","nullable":true},"timezone_id":{"type":"integer","description":"Alias per timezoneId","nullable":true},"kind":{"type":"string","enum":["OFFLINE","ONLINE"],"description":"OFFLINE (default) o ONLINE"},"status":{"type":"string","enum":["ENABLED","DISABLED"]},"openingHours":{"type":"array","description":"Configurazione orari: array di giorni con ranges [{start,end}] in formato HH:mm","example":[{"day":"MONDAY","ranges":[{"start":"09:00","end":"13:00"},{"start":"15:00","end":"19:00"}]},{"day":"SATURDAY","ranges":[{"start":"09:00","end":"12:30"}]}],"items":{"type":"object","properties":{"day":{"type":"string","enum":["MONDAY","TUESDAY","WEDNESDAY","THURSDAY","FRIDAY","SATURDAY","SUNDAY"]},"ranges":{"type":"array","items":{"type":"object","properties":{"start":{"type":"string","pattern":"^([01]\\d|2[0-3]):([0-5]\\d)$"},"end":{"type":"string","pattern":"^([01]\\d|2[0-3]):([0-5]\\d)$"}},"required":["start","end"]}}},"required":["day","ranges"]},"nullable":true},"categoryIds":{"type":"array","items":{"type":"integer"},"description":"ID categorie shop_categories"},"categories":{"type":"array","items":{"type":"integer"},"description":"Alias per categoryIds"},"website":{"type":"string","description":"Sito web negozio","nullable":true},"ecommerce":{"type":"string","description":"URL e-commerce","nullable":true},"logo":{"type":"string","description":"URL logo (es. da upload media shops)","nullable":true},"contactPerson":{"type":"string","description":"Persona di contatto / referente","nullable":true},"dynamicFields":{"type":"array","description":"Valori campi dinamici negozio (kind=SHOP)","items":{"type":"object","required":["dynamicFieldId"],"properties":{"dynamicFieldId":{"type":"integer"},"value":{"type":"string","nullable":true}}}}}}}},"required":true},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"201":{"description":"Risorsa creata (shops)","content":{"application/json":{"schema":{"description":"Risorsa creata (shops)","type":"object","additionalProperties":true,"example":{"id":1,"name":"Negozio centro","description":null,"companyName":null,"programId":1,"netId":1,"addressId":1,"address":{"id":1,"street":"Via Roma","number":"1","floor":null,"apartment":null,"zipCode":"50100","city":"Firenze","state":"FI","country":"IT","latitude":null,"longitude":null},"phone":"+39055123456","email":null,"foreignId":null,"taxNumber":null,"localCurrencyId":1,"timezoneId":2,"status":"ENABLED","website":"https://negozio.example","ecommerce":"https://shop.example","logo":"https://cdn.example/logo.png","contactPerson":"Mario Rossi","openingHours":[{"day":"MONDAY","ranges":[{"start":"09:00","end":"13:00"},{"start":"15:00","end":"19:00"}]},{"day":"SATURDAY","ranges":[{"start":"09:00","end":"12:30"}]}],"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z","lastTransactionAt":"2026-01-15T10:00:00.000Z","lastTerminalUseAt":"2026-01-15T10:00:00.000Z","newShop":false}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/shops/{id}":{"get":{"tags":["Shops"],"description":"Dettaglio negozio. Se l'indirizzo collegato non ha latitude/longitude, il backend tenta geocoding (Nominatim) e salva le coordinate su `addresses` prima della risposta. Con borsellino negozi attivo (`businessRules.shopWallet.enabled`), include `shopWalletBalances` (saldo per ogni wallet del programma). Con token CUSTOMER include `preferred`.\n\n**Contesto:** I **Negozi** sono i punti vendita del programma (fisici o online). Hanno indirizzo geocodificato, orari, categorie, terminali e possono avere un borsellino negozio dedicato.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n- `id` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Dettaglio shops\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Shops.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Dettaglio shops","content":{"application/json":{"schema":{"description":"Dettaglio shops","type":"object","additionalProperties":true,"example":{"id":1,"name":"Negozio centro","description":null,"companyName":null,"programId":1,"netId":1,"addressId":1,"address":{"id":1,"street":"Via Roma","number":"1","floor":null,"apartment":null,"zipCode":"50100","city":"Firenze","state":"FI","country":"IT","latitude":null,"longitude":null},"phone":"+39055123456","email":null,"foreignId":null,"taxNumber":null,"localCurrencyId":1,"timezoneId":2,"status":"ENABLED","website":"https://negozio.example","ecommerce":"https://shop.example","logo":"https://cdn.example/logo.png","contactPerson":"Mario Rossi","openingHours":[{"day":"MONDAY","ranges":[{"start":"09:00","end":"13:00"},{"start":"15:00","end":"19:00"}]},{"day":"SATURDAY","ranges":[{"start":"09:00","end":"12:30"}]}],"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z","lastTransactionAt":"2026-01-15T10:00:00.000Z","lastTerminalUseAt":"2026-01-15T10:00:00.000Z","newShop":false}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"put":{"tags":["Shops"],"description":"Aggiorna una programma fedeltà (campi consentiti) del programma indicato in `programId`.\n\n**Contesto:** I **Negozi** sono i punti vendita del programma (fisici o online). Hanno indirizzo geocodificato, orari, categorie, terminali e possono avere un borsellino negozio dedicato.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n- `id` (integer) — obbligatorio\n\n**Body JSON:**\n- `name` (string) — opzionale\n- `description` (string) — opzionale\n- `companyName` (string) — opzionale\n- `taxNumber` (string) — opzionale: Partita IVA / Codice Fiscale\n- `netId` (integer) — opzionale\n- `addressId` (integer) — opzionale\n- `phone` (string) — opzionale\n- `email` (string) — opzionale\n- `foreignId` (string) — opzionale\n- `localCurrencyId` (integer) — opzionale: Id moneta di default del negozio (override di program.localCurrencyId)\n- `timezoneId` (integer) — opzionale: Fuso orario negozio (id da GET /api/v1/time-zones). NULL = eredita da rete/sottorete o programma.\n- `timezone_id` (integer) — opzionale: Alias per timezoneId\n- `kind` (string) — opzionale: OFFLINE o ONLINE (valori: OFFLINE, ONLINE)\n- `status` (string) — opzionale (valori: ENABLED, DISABLED)\n- `openingHours` (array) — opzionale: Configurazione orari: array di giorni con ranges [{start,end}] in formato HH:mm\n- `categoryIds` (array) — opzionale: ID categorie (alias: categories)\n- `categories` (array) — opzionale: Alias per categoryIds\n- `website` (string) — opzionale\n- `ecommerce` (string) — opzionale\n- `logo` (string) — opzionale\n- `contactPerson` (string) — opzionale\n- `dynamicFields` (array) — opzionale: Valori campi dinamici negozio (kind=SHOP)\n\n**Risposte di successo:**\n- **200**: Aggiornamento shops\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Shops.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"description":{"type":"string"},"companyName":{"type":"string"},"taxNumber":{"type":"string","description":"Partita IVA / Codice Fiscale","nullable":true},"netId":{"type":"integer","nullable":true},"addressId":{"type":"integer","nullable":true},"phone":{"type":"string","nullable":true},"email":{"type":"string","format":"email","nullable":true},"foreignId":{"type":"string","nullable":true},"localCurrencyId":{"type":"integer","description":"Id moneta di default del negozio (override di program.localCurrencyId)","nullable":true},"timezoneId":{"type":"integer","description":"Fuso orario negozio (id da GET /api/v1/time-zones). NULL = eredita da rete/sottorete o programma.","nullable":true},"timezone_id":{"type":"integer","description":"Alias per timezoneId","nullable":true},"kind":{"type":"string","enum":["OFFLINE","ONLINE"],"description":"OFFLINE o ONLINE"},"status":{"type":"string","enum":["ENABLED","DISABLED"]},"openingHours":{"type":"array","description":"Configurazione orari: array di giorni con ranges [{start,end}] in formato HH:mm","example":[{"day":"MONDAY","ranges":[{"start":"09:00","end":"13:00"},{"start":"15:00","end":"19:00"}]},{"day":"SATURDAY","ranges":[{"start":"09:00","end":"12:30"}]}],"items":{"type":"object","properties":{"day":{"type":"string","enum":["MONDAY","TUESDAY","WEDNESDAY","THURSDAY","FRIDAY","SATURDAY","SUNDAY"]},"ranges":{"type":"array","items":{"type":"object","properties":{"start":{"type":"string","pattern":"^([01]\\d|2[0-3]):([0-5]\\d)$"},"end":{"type":"string","pattern":"^([01]\\d|2[0-3]):([0-5]\\d)$"}},"required":["start","end"]}}},"required":["day","ranges"]},"nullable":true},"categoryIds":{"type":"array","items":{"type":"integer"},"description":"ID categorie (alias: categories)"},"categories":{"type":"array","items":{"type":"integer"},"description":"Alias per categoryIds"},"website":{"type":"string","nullable":true},"ecommerce":{"type":"string","nullable":true},"logo":{"type":"string","nullable":true},"contactPerson":{"type":"string","nullable":true},"dynamicFields":{"type":"array","description":"Valori campi dinamici negozio (kind=SHOP)","items":{"type":"object","required":["dynamicFieldId"],"properties":{"dynamicFieldId":{"type":"integer"},"value":{"type":"string","nullable":true}}}}}}}}},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Aggiornamento shops","content":{"application/json":{"schema":{"description":"Aggiornamento shops","type":"object","additionalProperties":true,"example":{"id":1,"name":"Negozio centro","description":null,"companyName":null,"programId":1,"netId":1,"addressId":1,"address":{"id":1,"street":"Via Roma","number":"1","floor":null,"apartment":null,"zipCode":"50100","city":"Firenze","state":"FI","country":"IT","latitude":null,"longitude":null},"phone":"+39055123456","email":null,"foreignId":null,"taxNumber":null,"localCurrencyId":1,"timezoneId":2,"status":"ENABLED","website":"https://negozio.example","ecommerce":"https://shop.example","logo":"https://cdn.example/logo.png","contactPerson":"Mario Rossi","openingHours":[{"day":"MONDAY","ranges":[{"start":"09:00","end":"13:00"},{"start":"15:00","end":"19:00"}]},{"day":"SATURDAY","ranges":[{"start":"09:00","end":"12:30"}]}],"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z","lastTransactionAt":"2026-01-15T10:00:00.000Z","lastTerminalUseAt":"2026-01-15T10:00:00.000Z","newShop":false}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"delete":{"tags":["Shops"],"description":"Elimina una programma fedeltà del programma indicato in `programId` (soft delete o `TERMINATED` se vincoli FK).\n\n**Contesto:** I **Negozi** sono i punti vendita del programma (fisici o online). Hanno indirizzo geocodificato, orari, categorie, terminali e possono avere un borsellino negozio dedicato.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n- `id` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **204**: Operazione riuscita senza corpo di risposta.\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Shops.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"204":{"description":"Operazione riuscita senza corpo di risposta."},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/shopsByDistance":{"get":{"tags":["Shops"],"description":"Negozi con indirizzo geocodificato entro `radius` metri dal punto (`latitude`, `longitude`). Opzionali `distanceFromLatitude` / `distanceFromLongitude`: se entrambi presenti, `distanceMeters` in risposta è calcolata da quel punto (non influisce sul filtro né sull’ordinamento, basati su latitude/longitude). Con token CUSTOMER, ogni elemento include `preferred`.\n\n**Contesto:** I **Negozi** sono i punti vendita del programma (fisici o online). Hanno indirizzo geocodificato, orari, categorie, terminali e possono avere un borsellino negozio dedicato.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n\n**Querystring:**\n- `latitude` (number) — obbligatorio: Latitudine centro ricerca (WGS84)\n- `longitude` (number) — obbligatorio: Longitudine centro ricerca (WGS84)\n- `radius` (number) — obbligatorio: Raggio di ricerca in metri ( > 0 )\n- `distanceFromLatitude` (number) — opzionale: Solo per distanceMeters in risposta (WGS84); richiede anche distanceFromLongitude\n- `distanceFromLongitude` (number) — opzionale: Solo per distanceMeters in risposta (WGS84); richiede anche distanceFromLatitude\n\n**Risposte di successo:**\n- **200**: Negozi ordinati per distanza dal centro di ricerca (latitude/longitude). Ogni elemento: distanceMeters da distanceFrom* se passati, altrimenti dal centro di ricerca; distanceFromLatitude/Longitude indicano il punto usato per distanceMeters.\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Shops.","parameters":[{"schema":{"type":"number"},"in":"query","name":"latitude","required":true,"description":"Latitudine centro ricerca (WGS84)"},{"schema":{"type":"number"},"in":"query","name":"longitude","required":true,"description":"Longitudine centro ricerca (WGS84)"},{"schema":{"type":"number"},"in":"query","name":"radius","required":true,"description":"Raggio di ricerca in metri ( > 0 )"},{"schema":{"type":"number"},"in":"query","name":"distanceFromLatitude","required":false,"description":"Solo per distanceMeters in risposta (WGS84); richiede anche distanceFromLongitude"},{"schema":{"type":"number"},"in":"query","name":"distanceFromLongitude","required":false,"description":"Solo per distanceMeters in risposta (WGS84); richiede anche distanceFromLatitude"},{"schema":{"type":"integer"},"in":"path","name":"programId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Negozi ordinati per distanza dal centro di ricerca (latitude/longitude). Ogni elemento: distanceMeters da distanceFrom* se passati, altrimenti dal centro di ricerca; distanceFromLatitude/Longitude indicano il punto usato per distanceMeters.","content":{"application/json":{"schema":{"description":"Negozi ordinati per distanza dal centro di ricerca (latitude/longitude). Ogni elemento: distanceMeters da distanceFrom* se passati, altrimenti dal centro di ricerca; distanceFromLatitude/Longitude indicano il punto usato per distanceMeters.","type":"object","properties":{"content":{"type":"array","items":{"type":"object","additionalProperties":true}}},"example":{"content":[{"id":206,"name":"Conad City Via dei Calzaiuoli","description":"Punto vendita centro storico","companyName":"Conad Cooperativa Nord Ovest","programId":12,"netId":3,"addressId":8841,"address":{"id":8841,"street":"Via dei Calzaiuoli","number":"12r","floor":null,"apartment":null,"zipCode":"50122","city":"Firenze","state":"FI","country":"IT","latitude":43.77042,"longitude":11.25488},"phone":"+39055289112","email":"calzaiuoli@conad.it","foreignId":"EXT-SHOP-FI-001","taxNumber":"IT01234567890","localCurrencyId":1,"status":"ENABLED","openingHours":[{"day":"MONDAY","ranges":[{"start":"08:30","end":"21:00"}]},{"day":"TUESDAY","ranges":[{"start":"08:30","end":"21:00"}]},{"day":"SATURDAY","ranges":[{"start":"08:30","end":"21:00"}]}],"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z","lastTransactionAt":"2026-04-12T18:42:00.000Z","lastTerminalUseAt":"2026-04-12T18:41:55.000Z","distanceFromLatitude":43.77325,"distanceFromLongitude":11.25596,"distanceMeters":418},{"id":207,"name":"Farmacia Comunale Campo di Marte","description":null,"companyName":null,"programId":12,"netId":null,"addressId":8842,"address":{"id":8842,"street":"Via Mannelli","number":"15","floor":null,"apartment":null,"zipCode":"50137","city":"Firenze","state":"FI","country":"IT","latitude":43.77105,"longitude":11.27095},"phone":"+39055623400","email":null,"foreignId":null,"taxNumber":null,"localCurrencyId":1,"status":"ENABLED","openingHours":[{"day":"MONDAY","ranges":[{"start":"08:30","end":"13:00"},{"start":"15:30","end":"19:30"}]},{"day":"SUNDAY","ranges":[{"start":"09:00","end":"12:30"}]}],"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z","lastTransactionAt":null,"lastTerminalUseAt":null,"distanceFromLatitude":43.77325,"distanceFromLongitude":11.25596,"distanceMeters":1286}]},"additionalProperties":true}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/shops/{shopId}/wallet-balances":{"get":{"tags":["Shops"],"description":"Elenco paginato di wallet-balances del programma indicato in `programId`.\n\n**Contesto:** I **Negozi** sono i punti vendita del programma (fisici o online). Hanno indirizzo geocodificato, orari, categorie, terminali e possono avere un borsellino negozio dedicato.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n- `shopId` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Saldi borsellino negozio per ogni wallet del programma.\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Shops.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"shopId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Saldi borsellino negozio per ogni wallet del programma.","content":{"application/json":{"schema":{"description":"Saldi borsellino negozio per ogni wallet del programma.","type":"object","additionalProperties":true,"example":{"shopId":3,"balances":[{"walletId":1,"walletName":"Punti","walletSymbol":"PT","decimalPlaces":2,"balance":1500,"totalLoaded":5000}]}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/shops/{shopId}/wallet-recharges":{"get":{"tags":["Shops"],"description":"Elenco paginato di wallet-recharges del programma indicato in `programId`.\n\n**Contesto:** I **Negozi** sono i punti vendita del programma (fisici o online). Hanno indirizzo geocodificato, orari, categorie, terminali e possono avere un borsellino negozio dedicato.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n- `shopId` (integer) — obbligatorio\n\n**Querystring:**\n- `page` (integer) — opzionale\n- `size` (integer) — opzionale\n- `walletId` (integer) — opzionale\n\n**Risposte di successo:**\n- **200**: Storico ricariche borsellino negozio (paginato).\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Shops.","parameters":[{"schema":{"type":"integer","minimum":1},"in":"query","name":"page","required":false},{"schema":{"type":"integer","minimum":1,"maximum":100},"in":"query","name":"size","required":false},{"schema":{"type":"integer"},"in":"query","name":"walletId","required":false},{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"shopId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Storico ricariche borsellino negozio (paginato).","content":{"application/json":{"schema":{"description":"Storico ricariche borsellino negozio (paginato).","type":"object","additionalProperties":true,"example":{"content":[{"id":1,"walletId":1,"walletName":"Punti","walletSymbol":"PT","amount":1000,"balanceBefore":500,"balanceAfter":1500,"operatorId":1,"note":"Ricarica mensile","createdAt":"2026-05-15T10:00:00.000Z"}],"page":1,"size":20,"totalElements":1,"totalPages":1}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/shops/{shopId}/wallets/{walletId}/recharge":{"post":{"tags":["Shops"],"description":"Crea recharge del programma indicato in `programId`.\n\n**Contesto:** I **Negozi** sono i punti vendita del programma (fisici o online). Hanno indirizzo geocodificato, orari, categorie, terminali e possono avere un borsellino negozio dedicato.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n- `shopId` (integer) — obbligatorio\n- `walletId` (integer) — obbligatorio\n\n**Body JSON:**\n- `units` (number) — obbligatorio\n- `note` (string) — opzionale\n\n**Risposte di successo:**\n- **200**: Ricarica borsellino negozio.\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Shops.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["units"],"properties":{"units":{"type":"number","minimum":0.000001},"note":{"type":"string"}}}}},"required":true},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"shopId","required":true},{"schema":{"type":"integer"},"in":"path","name":"walletId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Ricarica borsellino negozio.","content":{"application/json":{"schema":{"description":"Ricarica borsellino negozio.","type":"object","additionalProperties":true,"example":{"transactionId":100,"shopId":3,"walletId":1,"units":1000,"balanceBefore":500,"balanceAfter":1500,"totalLoaded":5000,"createdAt":"2026-05-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/shops/{shopId}/wallets/{walletId}/adjust":{"post":{"tags":["Shops"],"description":"Crea adjust del programma indicato in `programId`.\n\n**Contesto:** I **Negozi** sono i punti vendita del programma (fisici o online). Hanno indirizzo geocodificato, orari, categorie, terminali e possono avere un borsellino negozio dedicato.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n- `shopId` (integer) — obbligatorio\n- `walletId` (integer) — obbligatorio\n\n**Body JSON:**\n- `units` (number) — obbligatorio\n- `note` (string) — opzionale\n\n**Risposte di successo:**\n- **200**: Rettifica saldo borsellino negozio.\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Shops.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["units"],"properties":{"units":{"type":"number"},"note":{"type":"string"}}}}},"required":true},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"shopId","required":true},{"schema":{"type":"integer"},"in":"path","name":"walletId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Rettifica saldo borsellino negozio.","content":{"application/json":{"schema":{"description":"Rettifica saldo borsellino negozio.","type":"object","additionalProperties":true,"example":{"transactionId":101,"shopId":3,"walletId":1,"units":-50,"balanceBefore":1500,"balanceAfter":1450,"createdAt":"2026-05-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/shops/{shopId}/wallet-weightings":{"get":{"tags":["Shops"],"description":"Elenco wallet con ponderazioni effettive per negozio (per tier). Token POS: solo shopId del terminale.\n\n**Contesto:** I **Negozi** sono i punti vendita del programma (fisici o online). Hanno indirizzo geocodificato, orari, categorie, terminali e possono avere un borsellino negozio dedicato.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n- `shopId` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Ponderazioni wallet per negozio (per tier).\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Shops.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"shopId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Ponderazioni wallet per negozio (per tier).","content":{"application/json":{"schema":{"description":"Ponderazioni wallet per negozio (per tier).","type":"object","additionalProperties":true,"example":{"shopId":3,"programId":1,"businessRules":{"shopsCanChangeWeighting":false,"enforceContractualFloor":true},"wallets":[{"walletId":1,"walletName":"Punti","walletSymbol":"PT","hasCustomWeighting":false,"summary":{"chargeUnitsPerAmount":1,"chargeAmountPerUnit":1,"chargeCurrencyId":1,"chargeCurrencyCode":"EUR","dischargeAmountPerUnit":0.01,"dischargeCurrencyId":1,"dischargeCurrencyCode":"EUR"},"tierSettings":[{"tierId":5,"tierName":"Gold","source":"wallet","customized":false,"effective":{"chargeUnitsPerAmount":1,"chargeAmountPerUnit":1},"shopOverride":null}]}],"tiers":[{"tierId":5,"tierName":"Gold"}]}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/shops/{shopId}/wallets/{walletId}/weightings":{"put":{"tags":["Shops"],"description":"Imposta o aggiorna ponderazioni negozio per un wallet (per tier). Token POS: solo shopId del terminale; requestedByShop implicito true.\n\n**Contesto:** I **Negozi** sono i punti vendita del programma (fisici o online). Hanno indirizzo geocodificato, orari, categorie, terminali e possono avere un borsellino negozio dedicato.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n- `shopId` (integer) — obbligatorio\n- `walletId` (integer) — obbligatorio\n\n**Body JSON:**\n- `tierSettings` (array) — opzionale\n- `requestedByShop` (boolean) — opzionale\n\n**Risposte di successo:**\n- **200**: Imposta ponderazioni negozio per un wallet.\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Shops.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"tierSettings":{"type":"array"},"requestedByShop":{"type":"boolean"}}}}}},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"shopId","required":true},{"schema":{"type":"integer"},"in":"path","name":"walletId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Imposta ponderazioni negozio per un wallet.","content":{"application/json":{"schema":{"description":"Imposta ponderazioni negozio per un wallet.","type":"object","additionalProperties":true,"example":{"wallet":{"walletId":1,"walletName":"Punti","walletSymbol":"PT","hasCustomWeighting":true,"summary":{"chargeUnitsPerAmount":1.1,"chargeAmountPerUnit":1},"tierSettings":[{"tierId":5,"tierName":"Gold","source":"shop","customized":true,"effective":{"chargeUnitsPerAmount":1.1},"shopOverride":{"chargeUnitsPerAmount":1.1}}]}}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/shops/{shopId}/pos-operators":{"get":{"tags":["Shops"],"description":"Elenco operatori di tipo POS del programma con accesso a questo negozio. Prima quelli con il negozio (o i suoi terminali) nello Scope, poi quelli della rete, infine quelli generici per tutto il programma.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"shopId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Operatori POS con accesso al negozio (specifici prima, poi generici).","content":{"application/json":{"schema":{"description":"Operatori POS con accesso al negozio (specifici prima, poi generici).","type":"object","additionalProperties":true,"example":{"shopId":3,"totalElements":2,"content":[{"id":12,"username":"cassa01","email":"cassa01@example.com","firstName":"Mario","lastName":"Rossi","operatorType":"POS","status":"ACTIVATE","programId":1,"lastUse":"2026-05-15T10:00:00.000Z","mustChangePassword":false,"language":"it","createdAt":"2026-05-15T10:00:00.000Z","updatedAt":"2026-05-15T10:00:00.000Z","scopeKind":"SHOP","scopeRefIds":[3],"accessKind":"shop","specific":true},{"id":4,"username":"pos.generico","email":null,"firstName":null,"lastName":null,"operatorType":"POS","status":"ACTIVATE","programId":1,"lastUse":null,"mustChangePassword":false,"language":"it","createdAt":"2026-05-15T10:00:00.000Z","updatedAt":"2026-05-15T10:00:00.000Z","scopeKind":null,"scopeRefIds":[],"accessKind":"program","specific":false}]}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/terminals":{"get":{"tags":["Terminals"],"description":"Elenco paginato di terminale POS del programma indicato in `programId`.\n\n**Contesto:** I **Terminali** identificano le casse/POS del negozio. Ogni terminale ha un `terminalCode` usato nel login POS e registra i movimenti con il proprio `terminalId`.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n\n**Querystring:**\n- `name` (string) — opzionale: Filtro per name (match parziale case-insensitive)\n- `description` (string) — opzionale: Filtro per description (match parziale case-insensitive)\n- `terminalCode` (string) — opzionale: Filtro per terminal_code (match parziale case-insensitive)\n- `foreignId` (string) — opzionale: Filtro per foreignId (match parziale case-insensitive)\n- `status` (string) — opzionale: Filtro stato terminale (valori: ENABLED, DISABLED, TERMINATED)\n- `page` (integer) — opzionale [default: 1]\n- `size` (integer) — opzionale [default: 20]\n\n**Risposte di successo:**\n- **200**: Terminali del programma\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Terminals.","parameters":[{"schema":{"type":"string"},"in":"query","name":"name","required":false,"description":"Filtro per name (match parziale case-insensitive)"},{"schema":{"type":"string"},"in":"query","name":"description","required":false,"description":"Filtro per description (match parziale case-insensitive)"},{"schema":{"type":"string"},"in":"query","name":"terminalCode","required":false,"description":"Filtro per terminal_code (match parziale case-insensitive)"},{"schema":{"type":"string"},"in":"query","name":"serialNumber","required":false,"description":"Filtro per serial_number hardware (match parziale case-insensitive)"},{"schema":{"type":"string"},"in":"query","name":"foreignId","required":false,"description":"Filtro per foreignId (match parziale case-insensitive)"},{"schema":{"type":"string","enum":["ENABLED","DISABLED","TERMINATED"]},"in":"query","name":"status","required":false,"description":"Filtro stato terminale"},{"schema":{"type":"integer","minimum":1,"default":1},"in":"query","name":"page","required":false},{"schema":{"type":"integer","minimum":1,"maximum":500,"default":20},"in":"query","name":"size","required":false},{"schema":{"type":"integer"},"in":"path","name":"programId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Terminali del programma","content":{"application/json":{"schema":{"description":"Terminali del programma","type":"object","additionalProperties":true,"example":{"content":[{"id":1,"name":"Cassa 1","description":null,"foreignId":null,"programId":1,"shopId":1,"apiKey":"trm_…","terminalCode":"A1B2C3D4","isActive":true,"status":"ENABLED","lastUse":"2026-01-15T10:00:00.000Z","softwareType":"WEB","softwareVersion":"1.1.0","createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}],"totalElements":1,"totalPages":1,"page":1,"size":20}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/terminals/grouped-by-shop":{"get":{"tags":["Terminals"],"description":"List all program terminals grouped by shop in a single response (avoids N+1 per-shop calls).\n\n**Contesto:** I **Terminali** identificano le casse/POS del negozio. Ogni terminale ha un `terminalCode` usato nel login POS e registra i movimenti con il proprio `terminalId`.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n\n**Querystring:**\n- `includeEmptyShops` (boolean) — opzionale: Include shops with no terminals (empty terminals array) [default: false]\n\n**Risposte di successo:**\n- **200**: Terminali del programma raggruppati per negozio.\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Terminals.","parameters":[{"schema":{"type":"boolean","default":false},"in":"query","name":"includeEmptyShops","required":false,"description":"Include shops with no terminals (empty terminals array)"},{"schema":{"type":"integer"},"in":"path","name":"programId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Terminali del programma raggruppati per negozio.","content":{"application/json":{"schema":{"description":"Terminali del programma raggruppati per negozio.","type":"object","additionalProperties":true,"example":{"programId":1,"totalElements":2,"terminals":[{"id":1,"name":"Cassa 1","description":null,"foreignId":null,"programId":1,"shopId":3,"apiKey":null,"terminalCode":"A1B2C3D4","isActive":true,"status":"ENABLED","lastUse":"2026-05-15T10:00:00.000Z","createdAt":"2026-05-15T10:00:00.000Z","updatedAt":"2026-05-15T10:00:00.000Z"}],"byShop":[{"shopId":3,"shopName":"Negozio centro","shopStatus":"ENABLED","terminals":[{"id":1,"name":"Cassa 1","shopId":3,"terminalCode":"A1B2C3D4","status":"ENABLED"}],"terminalsCount":1}]}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/shops/{shopId}/terminals":{"get":{"tags":["Terminals"],"description":"Elenco paginato di terminale POS del programma indicato in `programId`.\n\n**Contesto:** I **Terminali** identificano le casse/POS del negozio. Ogni terminale ha un `terminalCode` usato nel login POS e registra i movimenti con il proprio `terminalId`.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n- `shopId` (integer) — obbligatorio\n\n**Querystring:**\n- `page` (integer) — opzionale [default: 1]\n- `size` (integer) — opzionale [default: 20]\n\n**Risposte di successo:**\n- **200**: Terminali del negozio\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Terminals.","parameters":[{"schema":{"type":"integer","minimum":1,"default":1},"in":"query","name":"page","required":false},{"schema":{"type":"integer","minimum":1,"maximum":500,"default":20},"in":"query","name":"size","required":false},{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"shopId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Terminali del negozio","content":{"application/json":{"schema":{"description":"Terminali del negozio","type":"object","additionalProperties":true,"example":{"content":[{"id":1,"name":"Cassa 1","description":null,"foreignId":null,"programId":1,"shopId":1,"apiKey":"trm_…","terminalCode":"A1B2C3D4","isActive":true,"status":"ENABLED","lastUse":"2026-01-15T10:00:00.000Z","softwareType":"WEB","softwareVersion":"1.1.0","createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}],"totalElements":1,"totalPages":1,"page":1,"size":20}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"post":{"tags":["Terminals"],"description":"Crea terminale POS del programma indicato in `programId`.\n\n**Contesto:** I **Terminali** identificano le casse/POS del negozio. Ogni terminale ha un `terminalCode` usato nel login POS e registra i movimenti con il proprio `terminalId`.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n- `shopId` (integer) — obbligatorio\n\n**Body JSON:**\n- `name` (string) — obbligatorio\n- `description` (string) — opzionale\n- `foreignId` (string) — opzionale\n- `status` (string) — opzionale (valori: ENABLED, DISABLED)\n\n**Risposte di successo:**\n- **201**: Terminale creato\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Terminals.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["name"],"properties":{"name":{"type":"string"},"description":{"type":"string"},"foreignId":{"type":"string","nullable":true},"serialNumber":{"type":"string","description":"Serial number hardware del POS (es. PAX SN). Opzionale; se valorizzato deve essere univoco.","nullable":true},"status":{"type":"string","enum":["ENABLED","DISABLED"]}}}}},"required":true},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"shopId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"201":{"description":"Terminale creato","content":{"application/json":{"schema":{"description":"Terminale creato","type":"object","additionalProperties":true,"example":{"id":1,"name":"Cassa 1","description":null,"foreignId":null,"programId":1,"shopId":1,"apiKey":"trm_…","terminalCode":"A1B2C3D4","isActive":true,"status":"ENABLED","lastUse":"2026-01-15T10:00:00.000Z","softwareType":"WEB","softwareVersion":"1.1.0","createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/shops/{shopId}/terminals/{id}":{"get":{"tags":["Terminals"],"description":"Dettaglio di una programma fedeltà per identificativo del programma indicato in `programId`.\n\n**Contesto:** I **Terminali** identificano le casse/POS del negozio. Ogni terminale ha un `terminalCode` usato nel login POS e registra i movimenti con il proprio `terminalId`.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n- `shopId` (integer) — obbligatorio\n- `id` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Dettaglio terminale\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Terminals.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"shopId","required":true},{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Dettaglio terminale","content":{"application/json":{"schema":{"description":"Dettaglio terminale","type":"object","additionalProperties":true,"example":{"id":1,"name":"Cassa 1","description":null,"foreignId":null,"programId":1,"shopId":1,"apiKey":"trm_…","terminalCode":"A1B2C3D4","isActive":true,"status":"ENABLED","lastUse":"2026-01-15T10:00:00.000Z","softwareType":"WEB","softwareVersion":"1.1.0","createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"put":{"tags":["Terminals"],"description":"Aggiorna una programma fedeltà (campi consentiti) del programma indicato in `programId`.\n\n**Contesto:** I **Terminali** identificano le casse/POS del negozio. Ogni terminale ha un `terminalCode` usato nel login POS e registra i movimenti con il proprio `terminalId`.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n- `shopId` (integer) — obbligatorio\n- `id` (integer) — obbligatorio\n\n**Body JSON:**\n- `name` (string) — opzionale\n- `description` (string) — opzionale\n- `foreignId` (string) — opzionale\n- `status` (string) — opzionale (valori: ENABLED, DISABLED)\n\n**Risposte di successo:**\n- **200**: Terminale aggiornato\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Terminals.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"description":{"type":"string"},"foreignId":{"type":"string","nullable":true},"serialNumber":{"type":"string","description":"Serial number hardware del POS. Invia stringa vuota o null per rimuoverlo.","nullable":true},"status":{"type":"string","enum":["ENABLED","DISABLED"]}}}}}},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"shopId","required":true},{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Terminale aggiornato","content":{"application/json":{"schema":{"description":"Terminale aggiornato","type":"object","additionalProperties":true,"example":{"id":1,"name":"Cassa 1","description":null,"foreignId":null,"programId":1,"shopId":1,"apiKey":"trm_…","terminalCode":"A1B2C3D4","isActive":true,"status":"ENABLED","lastUse":"2026-01-15T10:00:00.000Z","softwareType":"WEB","softwareVersion":"1.1.0","createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"delete":{"tags":["Terminals"],"description":"Elimina una programma fedeltà del programma indicato in `programId` (soft delete o `TERMINATED` se vincoli FK).\n\n**Contesto:** I **Terminali** identificano le casse/POS del negozio. Ogni terminale ha un `terminalCode` usato nel login POS e registra i movimenti con il proprio `terminalId`.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n- `shopId` (integer) — obbligatorio\n- `id` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **204**: Operazione riuscita senza corpo di risposta.\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Terminals.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"shopId","required":true},{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"204":{"description":"Operazione riuscita senza corpo di risposta."},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/users":{"get":{"tags":["Users"],"description":"Elenco paginato di operatore del programma indicato in `programId`.\n\n**Contesto:** Gli **Operatori/Utenti** accedono al backoffice o al POS con username/password. Hanno ruoli e permessi IAM; possono essere legati a programma, rete o negozio.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n\n**Querystring:**\n- `page` (integer) — opzionale [default: 1]\n- `size` (integer) — opzionale [default: 20]\n- `search` (string) — opzionale\n\n**Risposte di successo:**\n- **200**: Lista users\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Users.","parameters":[{"schema":{"type":"integer","default":1},"in":"query","name":"page","required":false},{"schema":{"type":"integer","default":20},"in":"query","name":"size","required":false},{"schema":{"type":"string"},"in":"query","name":"search","required":false},{"schema":{"type":"integer"},"in":"path","name":"programId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Lista users","content":{"application/json":{"schema":{"description":"Lista users","type":"object","additionalProperties":true,"example":{"content":[{"id":1,"username":"operatore1","email":"op@example.com","programId":1,"roles":["CASSA"],"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}],"totalElements":1,"totalPages":1,"page":1,"size":20}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"post":{"tags":["Users"],"description":"Crea operatore del programma indicato in `programId`.\n\n**Contesto:** Gli **Operatori/Utenti** accedono al backoffice o al POS con username/password. Hanno ruoli e permessi IAM; possono essere legati a programma, rete o negozio.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n\n**Body JSON:**\n- `username` (string) — obbligatorio\n- `password` (string) — obbligatorio\n- `email` (string) — opzionale\n- `firstName` (string) — opzionale\n- `lastName` (string) — opzionale\n- `operatorType` (string) — opzionale (valori: POS, BACKEND, PLATFORM) [default: \"BACKEND\"]\n- `apiKey` (string) — opzionale: Solo per operatorType POS: API key del operatore (univoca). Se omessa, viene generata automaticamente.\n- `scopeKind` (string) — opzionale\n- `scopeRefIds` (array) — opzionale\n\n**Risposte di successo:**\n- **201**: Risorsa creata (users)\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Users.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["username","password"],"properties":{"username":{"type":"string"},"password":{"type":"string"},"email":{"type":"string","format":"email"},"firstName":{"type":"string"},"lastName":{"type":"string"},"operatorType":{"type":"string","enum":["POS","BACKEND","PLATFORM"],"default":"BACKEND"},"language":{"type":"string","enum":["it","en","es"],"default":"it"},"apiKey":{"type":"string","description":"Solo per operatorType POS: API key del operatore (univoca). Se omessa, viene generata automaticamente."},"scopeKind":{"type":"string","nullable":true},"scopeRefIds":{"type":"array","items":{"type":"integer"}}}}}},"required":true},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"201":{"description":"Risorsa creata (users)","content":{"application/json":{"schema":{"description":"Risorsa creata (users)","type":"object","additionalProperties":true,"example":{"id":1,"username":"operatore1","email":"op@example.com","programId":1,"roles":["CASSA"],"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/users/me":{"get":{"tags":["Users"],"description":"Elenco paginato di me.\n\n**Contesto:** Gli **Operatori/Utenti** accedono al backoffice o al POS con username/password. Hanno ruoli e permessi IAM; possono essere legati a programma, rete o negozio.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Risposte di successo:**\n- **200**: Profilo corrente\n\n**Errori documentati:** 400, 401, 403, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Users.","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Profilo corrente","content":{"application/json":{"schema":{"description":"Profilo corrente","type":"object","additionalProperties":true,"example":{"id":1,"username":"me","programId":1,"roles":[],"language":"it"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"put":{"tags":["Users"],"description":"Update current authenticated operator preferences (language).","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"language":{"type":"string","enum":["it","en","es"]}},"additionalProperties":false}}}},"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Preferenze operatore aggiornate","content":{"application/json":{"schema":{"description":"Preferenze operatore aggiornate","type":"object","additionalProperties":true,"example":{"id":1,"username":"op1","programId":1,"language":"en"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/users/me/verify-password":{"post":{"tags":["Users"],"description":"Verify current operator password without changing it.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["password"],"properties":{"password":{"type":"string","minLength":1}}}}},"required":true},"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"username":{"type":"string"}},"example":{"ok":false,"username":""}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/users/me/operator-nfc-tag":{"post":{"tags":["Users"],"description":"Associa un tag NFC (UID) all'operatore autenticato. Richiede password corretta. Usato dal POS per \"Assegna Tag Operatore\".","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["nfcUid","password"],"properties":{"nfcUid":{"type":"string"},"password":{"type":"string","minLength":1}}}}},"required":true},"security":[{"bearerAuth":[]}],"responses":{"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/users/me/operator-nfc-tag/unlink":{"post":{"tags":["Users"],"description":"Dissocia il tag NFC operatore dell'utente autenticato.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"nfcUid":{"type":"string","description":"Opzionale: UID specifico da scollegare"}}}}}},"security":[{"bearerAuth":[]}],"responses":{"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/users/{id}":{"get":{"tags":["Users"],"description":"Dettaglio di operatore per identificativo.\n\n**Contesto:** Gli **Operatori/Utenti** accedono al backoffice o al POS con username/password. Hanno ruoli e permessi IAM; possono essere legati a programma, rete o negozio.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `id` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Operatore\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Users.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Operatore","content":{"application/json":{"schema":{"description":"Operatore","type":"object","additionalProperties":true,"example":{"id":1,"username":"op1","email":"a@b.c","programId":1,"roles":[],"mustChangePassword":false,"language":"it"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"put":{"tags":["Users"],"description":"Aggiorna operatore (campi consentiti).\n\n**Contesto:** Gli **Operatori/Utenti** accedono al backoffice o al POS con username/password. Hanno ruoli e permessi IAM; possono essere legati a programma, rete o negozio.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `id` (integer) — obbligatorio\n\n**Body JSON:**\n- `email` (string) — opzionale\n- `firstName` (string) — opzionale\n- `lastName` (string) — opzionale\n- `operatorType` (string) — opzionale (valori: POS, BACKEND, PLATFORM)\n- `status` (string) — opzionale (valori: NON_ACTIVATE, ACTIVATE, BLOCKED, MUST_CHANGE_PASSWORD)\n- `scopeKind` (string) — opzionale\n- `scopeRefIds` (array) — opzionale\n\n**Risposte di successo:**\n- **200**: Operatore aggiornato\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Users.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":"string","format":"email"},"firstName":{"type":"string"},"lastName":{"type":"string"},"operatorType":{"type":"string","enum":["POS","BACKEND","PLATFORM"]},"status":{"type":"string","enum":["NON_ACTIVATE","ACTIVATE","BLOCKED","MUST_CHANGE_PASSWORD"]},"language":{"type":"string","enum":["it","en","es"]},"scopeKind":{"type":"string","nullable":true},"scopeRefIds":{"type":"array","items":{"type":"integer"}}}}}}},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Operatore aggiornato","content":{"application/json":{"schema":{"description":"Operatore aggiornato","type":"object","additionalProperties":true,"example":{"id":1,"username":"op1","programId":1,"language":"it"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"delete":{"tags":["Users"],"description":"Elimina operatore (soft delete o `TERMINATED` se vincoli FK).\n\n**Contesto:** Gli **Operatori/Utenti** accedono al backoffice o al POS con username/password. Hanno ruoli e permessi IAM; possono essere legati a programma, rete o negozio.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `id` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **204**: Operazione riuscita senza corpo di risposta.\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Users.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"204":{"description":"Operazione riuscita senza corpo di risposta."},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/users/{id}/programs":{"put":{"tags":["Users"],"description":"Sustituye los programas que un operador puede abrir. Solo operadores de sistema.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["programIds"],"properties":{"programIds":{"type":"array","minItems":1,"items":{"type":"integer"}}}}}},"required":true},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/users/{id}/changepassword":{"put":{"tags":["Users"],"description":"Change password. Operators (BACKEND/PLATFORM/POS) and CUSTOMER tokens may only change their own password (token sub = id).\n\n**Contesto:** Gli **Operatori/Utenti** accedono al backoffice o al POS con username/password. Hanno ruoli e permessi IAM; possono essere legati a programma, rete o negozio.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `id` (integer) — obbligatorio\n\n**Body JSON:**\n- `oldPassword` (string) — obbligatorio: Current password\n- `newPassword` (string) — obbligatorio: New password\n\n**Risposte di successo:**\n- **200**: Password cambiata. Il nuovo access_token restituito NON contiene il claim mustChangePassword: il client deve sostituire il precedente con questo per continuare ad usare gli altri endpoint. Refresh token: quello già rilasciato al login resta valido.\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Users.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["oldPassword","newPassword"],"properties":{"oldPassword":{"type":"string","description":"Current password"},"newPassword":{"type":"string","minLength":1,"description":"New password"}}}}},"required":true},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Password cambiata. Il nuovo access_token restituito NON contiene il claim mustChangePassword: il client deve sostituire il precedente con questo per continuare ad usare gli altri endpoint. Refresh token: quello già rilasciato al login resta valido.","content":{"application/json":{"schema":{"description":"Password cambiata. Il nuovo access_token restituito NON contiene il claim mustChangePassword: il client deve sostituire il precedente con questo per continuare ad usare gli altri endpoint. Refresh token: quello già rilasciato al login resta valido.","type":"object","additionalProperties":true,"example":{"access_token":"eyJ…","token_type":"Bearer","expires_in":600,"mustChangePassword":false}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/users/reset-password":{"post":{"tags":["Users"],"description":"Reset user password. Generates a temporary password and sends it via email. The user must change it on first login.\n\n**Contesto:** Gli **Operatori/Utenti** accedono al backoffice o al POS con username/password. Hanno ruoli e permessi IAM; possono essere legati a programma, rete o negozio.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Body JSON:**\n- `id` (integer) — obbligatorio\n- `email` (string) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Reset password operatore: genera password temporanea inviata via email.\n\n**Errori documentati:** 400, 401, 403, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Users.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["id","email"],"properties":{"id":{"type":"integer"},"email":{"type":"string","format":"email"}}}}},"required":true},"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Reset password operatore: genera password temporanea inviata via email.","content":{"application/json":{"schema":{"description":"Reset password operatore: genera password temporanea inviata via email.","type":"object","additionalProperties":true,"example":{"ok":true}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/roles":{"get":{"tags":["Roles"],"description":"Elenco paginato di ruolo del programma indicato in `programId`.\n\n**Contesto:** I **Ruoli** raggruppano permessi granulari (stile IAM). Si assegnano agli operatori per definire cosa possono leggere o modificare nel programma.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n\n**Querystring:**\n- `page` (integer) — opzionale [default: 1]\n- `size` (integer) — opzionale [default: 20]\n\n**Risposte di successo:**\n- **200**: Lista roles\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Roles.","parameters":[{"schema":{"type":"integer","minimum":1,"default":1},"in":"query","name":"page","required":false},{"schema":{"type":"integer","minimum":1,"maximum":500,"default":20},"in":"query","name":"size","required":false},{"schema":{"type":"integer"},"in":"path","name":"programId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Lista roles","content":{"application/json":{"schema":{"description":"Lista roles","type":"object","additionalProperties":true,"example":{"content":[{"id":1,"name":"Operatore cassa","description":null,"programId":1,"permissions":[{"id":1,"code":"MOVEMENTS_CREATE","name":"Crea movimenti","description":null,"section":"movements"}],"userCount":3,"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}],"totalElements":1,"totalPages":1,"page":1,"size":20}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"post":{"tags":["Roles"],"description":"Crea ruolo del programma indicato in `programId`.\n\n**Contesto:** I **Ruoli** raggruppano permessi granulari (stile IAM). Si assegnano agli operatori per definire cosa possono leggere o modificare nel programma.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n\n**Body JSON:**\n- `name` (string) — obbligatorio\n- `description` (string) — opzionale\n- `permissionIds` (array) — opzionale\n\n**Risposte di successo:**\n- **201**: Risorsa creata (roles)\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Roles.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["name"],"properties":{"name":{"type":"string"},"description":{"type":"string"},"permissionIds":{"type":"array","items":{"type":"integer"}}}}}},"required":true},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"201":{"description":"Risorsa creata (roles)","content":{"application/json":{"schema":{"description":"Risorsa creata (roles)","type":"object","additionalProperties":true,"example":{"id":1,"name":"Operatore cassa","description":null,"programId":1,"permissions":[{"id":1,"code":"MOVEMENTS_CREATE","name":"Crea movimenti","description":null,"section":"movements"}],"userCount":3,"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/roles":{"get":{"tags":["Roles"],"description":"Elenco paginato di ruolo.\n\n**Contesto:** I **Ruoli** raggruppano permessi granulari (stile IAM). Si assegnano agli operatori per definire cosa possono leggere o modificare nel programma.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Risposte di successo:**\n- **200**: Ruoli di sistema\n\n**Errori documentati:** 400, 401, 403, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Roles.","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Ruoli di sistema","content":{"application/json":{"schema":{"description":"Ruoli di sistema","type":"object","additionalProperties":true,"example":[{"id":1,"name":"SYSTEM_ADMIN","description":null}]}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/roles/{id}":{"get":{"tags":["Roles"],"description":"Dettaglio di una programma fedeltà per identificativo del programma indicato in `programId`.\n\n**Contesto:** I **Ruoli** raggruppano permessi granulari (stile IAM). Si assegnano agli operatori per definire cosa possono leggere o modificare nel programma.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n- `id` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Dettaglio roles\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Roles.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Dettaglio roles","content":{"application/json":{"schema":{"description":"Dettaglio roles","type":"object","additionalProperties":true,"example":{"id":1,"name":"Operatore cassa","description":null,"programId":1,"permissions":[{"id":1,"code":"MOVEMENTS_CREATE","name":"Crea movimenti","description":null,"section":"movements"}],"userCount":3,"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"put":{"tags":["Roles"],"description":"Aggiorna una programma fedeltà (campi consentiti) del programma indicato in `programId`.\n\n**Contesto:** I **Ruoli** raggruppano permessi granulari (stile IAM). Si assegnano agli operatori per definire cosa possono leggere o modificare nel programma.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n- `id` (integer) — obbligatorio\n\n**Body JSON:**\n- `name` (string) — opzionale\n- `description` (string) — opzionale\n- `permissionIds` (array) — opzionale\n\n**Risposte di successo:**\n- **200**: Aggiornamento roles\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Roles.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"description":{"type":"string"},"permissionIds":{"type":"array","items":{"type":"integer"}}}}}}},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Aggiornamento roles","content":{"application/json":{"schema":{"description":"Aggiornamento roles","type":"object","additionalProperties":true,"example":{"id":1,"name":"Operatore cassa","description":null,"programId":1,"permissions":[{"id":1,"code":"MOVEMENTS_CREATE","name":"Crea movimenti","description":null,"section":"movements"}],"userCount":3,"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"delete":{"tags":["Roles"],"description":"Elimina una programma fedeltà del programma indicato in `programId` (soft delete o `TERMINATED` se vincoli FK).\n\n**Contesto:** I **Ruoli** raggruppano permessi granulari (stile IAM). Si assegnano agli operatori per definire cosa possono leggere o modificare nel programma.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n- `id` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **204**: Operazione riuscita senza corpo di risposta.\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Roles.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"204":{"description":"Operazione riuscita senza corpo di risposta."},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/users/{userId}/roles":{"get":{"tags":["Roles"],"description":"Elenco paginato di ruolo.\n\n**Contesto:** I **Ruoli** raggruppano permessi granulari (stile IAM). Si assegnano agli operatori per definire cosa possono leggere o modificare nel programma.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `userId` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Ruoli assegnati\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Roles.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"userId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Ruoli assegnati","content":{"application/json":{"schema":{"description":"Ruoli assegnati","type":"object","additionalProperties":true,"example":[{"id":1,"name":"Cassa","programId":1}]}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"post":{"tags":["Roles"],"description":"Crea ruolo.\n\n**Contesto:** I **Ruoli** raggruppano permessi granulari (stile IAM). Si assegnano agli operatori per definire cosa possono leggere o modificare nel programma.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `userId` (integer) — obbligatorio\n\n**Body JSON:**\n- `roleId` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **201**: Ruolo assegnato\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Roles.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["roleId"],"properties":{"roleId":{"type":"integer"}}}}},"required":true},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"userId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"201":{"description":"Ruolo assegnato","content":{"application/json":{"schema":{"description":"Ruolo assegnato","type":"object","additionalProperties":true,"example":{"userId":1,"roleId":1}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/users/{userId}/roles/{roleId}":{"delete":{"tags":["Roles"],"description":"Elimina ruolo (soft delete o `TERMINATED` se vincoli FK).\n\n**Contesto:** I **Ruoli** raggruppano permessi granulari (stile IAM). Si assegnano agli operatori per definire cosa possono leggere o modificare nel programma.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `userId` (integer) — obbligatorio\n- `roleId` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **204**: Operazione riuscita senza corpo di risposta.\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Roles.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"userId","required":true},{"schema":{"type":"integer"},"in":"path","name":"roleId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"204":{"description":"Operazione riuscita senza corpo di risposta."},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/permissions":{"get":{"tags":["Permissions"],"description":"Elenco paginato di permesso.\n\n**Contesto:** I **Permessi** sono le capability atomiche del sistema (es. `PROGRAMS_READ`, `MOVEMENTS_CREATE`), raggruppate per sezione e associabili ai ruoli.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Querystring:**\n- `section` (string) — opzionale\n\n**Risposte di successo:**\n- **200**: Elenco permessi (array)\n\n**Errori documentati:** 400, 401, 403, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Permissions.","parameters":[{"schema":{"type":"string"},"in":"query","name":"section","required":false}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Elenco permessi (array)","content":{"application/json":{"schema":{"description":"Elenco permessi (array)","type":"object","additionalProperties":true,"example":[{"id":1,"code":"PROGRAMS_READ","name":"Lettura programmi","description":null,"section":"programs","isDefault":false}]}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/users/{id}/permissions":{"get":{"tags":["Permissions"],"description":"Elenco paginato di permesso.\n\n**Contesto:** I **Permessi** sono le capability atomiche del sistema (es. `PROGRAMS_READ`, `MOVEMENTS_CREATE`), raggruppate per sezione e associabili ai ruoli.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `id` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Permessi utente\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Permissions.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Permessi utente","content":{"application/json":{"schema":{"description":"Permessi utente","type":"object","additionalProperties":true,"example":{"userId":1,"username":"op","programId":1,"permissions":[{"id":1,"code":"PROGRAMS_READ","name":"Lettura programmi","description":null,"section":"programs","isDefault":false}]}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/permissions/{id}":{"get":{"tags":["Permissions"],"description":"Dettaglio di permesso per identificativo.\n\n**Contesto:** I **Permessi** sono le capability atomiche del sistema (es. `PROGRAMS_READ`, `MOVEMENTS_CREATE`), raggruppate per sezione e associabili ai ruoli.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `id` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Permesso\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Permissions.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Permesso","content":{"application/json":{"schema":{"description":"Permesso","type":"object","additionalProperties":true,"example":{"id":1,"code":"PROGRAMS_READ","name":"Lettura programmi","description":null,"section":"programs","isDefault":false}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/countries":{"get":{"tags":["Countries"],"description":"Elenco paginato di paese.\n\n**Contesto:** Anagrafica **paesi** con codice ISO, prefisso telefonico e metadati usati in indirizzi e registrazione clienti.\n\n**Autenticazione:** endpoint pubblico — non richiede `Authorization: Bearer` (può richiedere `X-API-Key` e/o OTP a seconda del caso).\n\n**Risposte di successo:**\n- **200**: Paesi\n\n**Errori documentati:** 400, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Countries.","responses":{"200":{"description":"Paesi","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"id":{"type":"integer"},"nome":{"type":"string"},"sigla":{"type":"string"},"prefissoTelefonico":{"type":"string"},"bandierina":{"type":"string"}}},"description":"Paesi","example":[{"id":1,"isoCode":"IT","name":"Italia","phonePrefix":"+39"}]}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/countries/{id}":{"get":{"tags":["Countries"],"description":"Dettaglio di paese per identificativo.\n\n**Contesto:** Anagrafica **paesi** con codice ISO, prefisso telefonico e metadati usati in indirizzi e registrazione clienti.\n\n**Autenticazione:** endpoint pubblico — non richiede `Authorization: Bearer` (può richiedere `X-API-Key` e/o OTP a seconda del caso).\n\n**Parametri path:**\n- `id` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Paese\n\n**Errori documentati:** 400, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Countries.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Paese","content":{"application/json":{"schema":{"description":"Paese","type":"object","additionalProperties":true,"example":{"id":1,"isoCode":"IT","name":"Italia","phonePrefix":"+39"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/addresses/search":{"get":{"tags":["Addresses"],"description":"Elenco paginato di search.\n\n**Contesto:** Gli **Indirizzi** strutturati (via, CAP, città, coordinate) collegati a clienti, negozi e ONG; supportano geocoding.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Querystring:**\n- `q` (string) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Ricerca indirizzi per autocomplete (Nominatim + Photon). Fino a 8 suggerimenti.\n\n**Errori documentati:** 400, 401, 403, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Addresses.","parameters":[{"schema":{"type":"string","minLength":3},"in":"query","name":"q","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Ricerca indirizzi per autocomplete (Nominatim + Photon). Fino a 8 suggerimenti.","content":{"application/json":{"schema":{"description":"Ricerca indirizzi per autocomplete (Nominatim + Photon). Fino a 8 suggerimenti.","type":"object","additionalProperties":true,"example":{"results":[{"place_id":123456789,"lat":"43.9356","lon":"11.4608","display_name":"Via Roma, 1, Borgo San Lorenzo, Firenze, Toscana, Italia","name":"Via Roma","type":"residential","address":{"road":"Via Roma","house_number":"1","postcode":"50032","city":"Borgo San Lorenzo","state":"Toscana","country_code":"it"}}]}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/addresses/resolve":{"get":{"tags":["Addresses"],"description":"Elenco paginato di resolve.\n\n**Contesto:** Gli **Indirizzi** strutturati (via, CAP, città, coordinate) collegati a clienti, negozi e ONG; supportano geocoding.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Querystring:**\n- `q` (string) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Risolve un indirizzo testuale in coordinate e campi strutturati.\n\n**Errori documentati:** 400, 401, 403, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Addresses.","parameters":[{"schema":{"type":"string","minLength":3},"in":"query","name":"q","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Risolve un indirizzo testuale in coordinate e campi strutturati.","content":{"application/json":{"schema":{"description":"Risolve un indirizzo testuale in coordinate e campi strutturati.","type":"object","additionalProperties":true,"example":{"result":{"place_id":123456789,"lat":"43.9356","lon":"11.4608","display_name":"Centro Commerciale Mugello","name":"Centro Commerciale Mugello","type":"mall","address":{"road":"Piazza Martin Luther King","house_number":"","postcode":"50032","city":"Borgo San Lorenzo","state":"Toscana","country_code":"it"}}}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/addresses":{"get":{"tags":["Addresses"],"description":"Elenco paginato di indirizzo.\n\n**Contesto:** Gli **Indirizzi** strutturati (via, CAP, città, coordinate) collegati a clienti, negozi e ONG; supportano geocoding.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Querystring:**\n- `city` (string) — opzionale\n- `country` (string) — opzionale: Filter by country code (e.g. IT, US)\n- `page` (integer) — opzionale [default: 1]\n- `size` (integer) — opzionale [default: 20]\n\n**Risposte di successo:**\n- **200**: Indirizzi\n\n**Errori documentati:** 400, 401, 403, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Addresses.","parameters":[{"schema":{"type":"string"},"in":"query","name":"city","required":false},{"schema":{"type":"string"},"in":"query","name":"country","required":false,"description":"Filter by country code (e.g. IT, US)"},{"schema":{"type":"integer","default":1},"in":"query","name":"page","required":false},{"schema":{"type":"integer","default":20},"in":"query","name":"size","required":false}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Indirizzi","content":{"application/json":{"schema":{"description":"Indirizzi","type":"object","additionalProperties":true,"example":{"content":[{"id":1,"street":"Via Roma","number":"10","floor":null,"apartment":null,"zipCode":"50100","city":"Firenze","state":"FI","country":"IT","countryId":1,"latitude":43.77,"longitude":11.25,"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}],"totalElements":1,"totalPages":1,"page":1,"size":20}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"post":{"tags":["Addresses"],"description":"Create address. Country is the ISO code (e.g. IT, US) and must exist in countries table. Use the returned id as addressId when creating/updating a shop.\n\n**Contesto:** Gli **Indirizzi** strutturati (via, CAP, città, coordinate) collegati a clienti, negozi e ONG; supportano geocoding.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Body JSON:**\n- `street` (string) — opzionale\n- `number` (string) — opzionale\n- `floor` (string) — opzionale\n- `apartment` (string) — opzionale\n- `zipCode` (string) — opzionale\n- `city` (string) — opzionale\n- `state` (string) — opzionale: Provincia (sigla ISTAT per IT, es. FI, BO, RM)\n- `country` (string) — opzionale: Country code (sigla) from countries table, e.g. IT, US\n- `latitude` (number) — opzionale\n- `longitude` (number) — opzionale\n\n**Risposte di successo:**\n- **201**: Indirizzo creato\n\n**Errori documentati:** 400, 401, 403, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Addresses.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"street":{"type":"string"},"number":{"type":"string"},"floor":{"type":"string"},"apartment":{"type":"string"},"zipCode":{"type":"string"},"city":{"type":"string"},"state":{"type":"string","description":"Provincia (sigla ISTAT per IT, es. FI, BO, RM)"},"country":{"type":"string","description":"Country code (sigla) from countries table, e.g. IT, US"},"latitude":{"type":"number","nullable":true},"longitude":{"type":"number","nullable":true}}}}}},"security":[{"bearerAuth":[]}],"responses":{"201":{"description":"Indirizzo creato","content":{"application/json":{"schema":{"description":"Indirizzo creato","type":"object","additionalProperties":true,"example":{"id":1,"street":"Via Roma","number":"10","floor":null,"apartment":null,"zipCode":"50100","city":"Firenze","state":"FI","country":"IT","countryId":1,"latitude":43.77,"longitude":11.25,"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/addresses/{id}":{"get":{"tags":["Addresses"],"description":"Dettaglio di indirizzo per identificativo.\n\n**Contesto:** Gli **Indirizzi** strutturati (via, CAP, città, coordinate) collegati a clienti, negozi e ONG; supportano geocoding.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `id` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Indirizzo\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Addresses.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Indirizzo","content":{"application/json":{"schema":{"description":"Indirizzo","type":"object","additionalProperties":true,"example":{"id":1,"street":"Via Roma","number":"10","floor":null,"apartment":null,"zipCode":"50100","city":"Firenze","state":"FI","country":"IT","countryId":1,"latitude":43.77,"longitude":11.25,"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"put":{"tags":["Addresses"],"description":"Update address. Country is the ISO code (e.g. IT, US) and must exist in countries table.\n\n**Contesto:** Gli **Indirizzi** strutturati (via, CAP, città, coordinate) collegati a clienti, negozi e ONG; supportano geocoding.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `id` (integer) — obbligatorio\n\n**Body JSON:**\n- `street` (string) — opzionale\n- `number` (string) — opzionale\n- `floor` (string) — opzionale\n- `apartment` (string) — opzionale\n- `zipCode` (string) — opzionale\n- `city` (string) — opzionale\n- `state` (string) — opzionale: Provincia (sigla ISTAT per IT, es. FI, BO, RM)\n- `country` (string) — opzionale: Country code (sigla) from countries table\n- `latitude` (number) — opzionale\n- `longitude` (number) — opzionale\n\n**Risposte di successo:**\n- **200**: Indirizzo aggiornato\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Addresses.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"street":{"type":"string"},"number":{"type":"string"},"floor":{"type":"string"},"apartment":{"type":"string"},"zipCode":{"type":"string"},"city":{"type":"string"},"state":{"type":"string","description":"Provincia (sigla ISTAT per IT, es. FI, BO, RM)"},"country":{"type":"string","description":"Country code (sigla) from countries table"},"latitude":{"type":"number","nullable":true},"longitude":{"type":"number","nullable":true}}}}}},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Indirizzo aggiornato","content":{"application/json":{"schema":{"description":"Indirizzo aggiornato","type":"object","additionalProperties":true,"example":{"id":1,"street":"Via Roma","number":"10","floor":null,"apartment":null,"zipCode":"50100","city":"Firenze","state":"FI","country":"IT","countryId":1,"latitude":43.77,"longitude":11.25,"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"delete":{"tags":["Addresses"],"description":"Elimina indirizzo (soft delete o `TERMINATED` se vincoli FK).\n\n**Contesto:** Gli **Indirizzi** strutturati (via, CAP, città, coordinate) collegati a clienti, negozi e ONG; supportano geocoding.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `id` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **204**: Operazione riuscita senza corpo di risposta.\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Addresses.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"204":{"description":"Operazione riuscita senza corpo di risposta."},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/customers/sendOtp":{"post":{"tags":["Customers"],"description":"Crea sendOtp.\n\n**Contesto:** I **Clienti** sono gli iscritti al programma fedeltà: anagrafica, tier, carte, saldi wallet, consensi, promo vinte e storico movimenti.\n\n**Autenticazione:** endpoint pubblico — non richiede `Authorization: Bearer` (può richiedere `X-API-Key` e/o OTP a seconda del caso).\n\n**Body JSON:**\n- `programId` (integer) — obbligatorio\n- `email` (string) — opzionale\n- `mobile` (string) — opzionale\n- `devicetype` (string) — opzionale\n\n**Risposte di successo:**\n- **200**: Operazione accettata.\n\n**Errori documentati:** 400, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Customers.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["programId"],"properties":{"programId":{"type":"integer"},"email":{"type":"string","format":"email"},"mobile":{"type":"string"},"devicetype":{"type":"string"}}}}},"required":true},"security":[{"apiKey":[]}],"responses":{"200":{"description":"Operazione accettata.","content":{"application/json":{"schema":{"description":"Operazione accettata.","type":"object","properties":{"ok":{"type":"boolean","example":true}},"example":{"ok":true}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/customers/resetPassword":{"post":{"tags":["Customers"],"description":"Crea resetPassword.\n\n**Contesto:** I **Clienti** sono gli iscritti al programma fedeltà: anagrafica, tier, carte, saldi wallet, consensi, promo vinte e storico movimenti.\n\n**Autenticazione:** endpoint pubblico — non richiede `Authorization: Bearer` (può richiedere `X-API-Key` e/o OTP a seconda del caso).\n\n**Body JSON:**\n- `programId` (integer) — obbligatorio\n- `email` (string) — opzionale\n- `mobile` (string) — opzionale\n- `devicetype` (string) — opzionale\n- `otp_code` (string) — obbligatorio\n- `password` (string) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Operazione accettata.\n\n**Errori documentati:** 400, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Customers.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["programId","otp_code","password"],"properties":{"programId":{"type":"integer"},"email":{"type":"string","format":"email"},"mobile":{"type":"string"},"devicetype":{"type":"string"},"otp_code":{"type":"string"},"password":{"type":"string","format":"password"}}}}},"required":true},"security":[{"apiKey":[]}],"responses":{"200":{"description":"Operazione accettata.","content":{"application/json":{"schema":{"description":"Operazione accettata.","type":"object","properties":{"ok":{"type":"boolean","example":true}},"example":{"ok":true}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/customers/verifyOtp":{"post":{"tags":["Customers"],"description":"Verify OTP sent to a new email address (via POST /api/v1/customers/sendOtp) and update the authenticated customer email. Requires Bearer CUSTOMER JWT and X-API-Key. Equivalent to legacy validateEmailByOtp.\n\n**Contesto:** I **Clienti** sono gli iscritti al programma fedeltà: anagrafica, tier, carte, saldi wallet, consensi, promo vinte e storico movimenti.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Body JSON:**\n- `programId` (integer) — obbligatorio\n- `mobile` (string) — opzionale\n- `email` (string) — obbligatorio\n- `otp_code` (string) — obbligatorio\n- `devicetype` (string) — opzionale\n\n**Risposte di successo:**\n- **200**: Operazione accettata.\n\n**Errori documentati:** 400, 401, 403, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Customers.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["programId","email","otp_code"],"properties":{"programId":{"type":"integer"},"mobile":{"type":"string"},"email":{"type":"string","format":"email"},"otp_code":{"type":"string"},"devicetype":{"type":"string"}}}}},"required":true},"security":[{"apiKey":[],"bearerAuth":[]}],"responses":{"200":{"description":"Operazione accettata.","content":{"application/json":{"schema":{"description":"Operazione accettata.","type":"object","properties":{"ok":{"type":"boolean","example":true}},"example":{"ok":true}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/customers/{id}/changePhone":{"put":{"tags":["Customers"],"description":"Change customer phone after OTP verification (sent via POST /api/v1/customers/sendOtp to the new number). Requires Bearer CUSTOMER JWT; path id must match token sub. Updates username when it uses mobile/cell format.\n\n**Contesto:** I **Clienti** sono gli iscritti al programma fedeltà: anagrafica, tier, carte, saldi wallet, consensi, promo vinte e storico movimenti.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `id` (integer) — obbligatorio\n\n**Body JSON:**\n- `phone` (string) — obbligatorio\n- `otp` (string) — obbligatorio\n- `otp_code` (string) — opzionale\n\n**Risposte di successo:**\n- **200**: Telefono aggiornato dopo verifica OTP.\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Customers.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["phone","otp"],"properties":{"phone":{"type":"string"},"otp":{"type":"string"},"otp_code":{"type":"string"}}}}},"required":true},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Telefono aggiornato dopo verifica OTP.","content":{"application/json":{"schema":{"description":"Telefono aggiornato dopo verifica OTP.","type":"object","additionalProperties":true,"example":{"ok":true,"phone":"+5492235067280","username":"+5492235067280","usernameUpdated":true}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/customers/changePassword":{"post":{"tags":["Customers"],"description":"Change password for the authenticated customer. Requires Bearer JWT with operatorType CUSTOMER; only the new password is required in the body.\n\n**Contesto:** I **Clienti** sono gli iscritti al programma fedeltà: anagrafica, tier, carte, saldi wallet, consensi, promo vinte e storico movimenti.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Body JSON:**\n- `password` (string) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Password aggiornata.\n\n**Errori documentati:** 400, 401, 403, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Customers.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["password"],"properties":{"password":{"type":"string","format":"password","minLength":1}}}}},"required":true},"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Password aggiornata.","content":{"application/json":{"schema":{"description":"Password aggiornata.","type":"object","additionalProperties":true,"example":{"ok":true}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/customers/customerCreate":{"post":{"tags":["Customers"],"description":"Endpoint pubblico: **non** richiede `Authorization: Bearer`. Sicurezza: header `X-API-Key` valido per il programma + `otp_code` verificato. Opzionali `username` e `password` (entrambi): come POST /customers/{id}/setUsernamePassword (access_status ACTIVATE). Se il cliente è **già registrato con password**, risponde **409** con `passwordOk` (`\"true\"` se la `password` inviata coincide, altrimenti `\"false\"`). Se il cellulare è già registrato **senza password** e l'OTP è valido, salva la nuova password e risponde **200** con `ok: true` e `passwordOk: \"new\"`. **Prima dell'INSERT** si cerca un cliente con stesso email/telefono nel programma: se esiste solo in stato «incompleto» (senza credenziali, `access_status` NON_ACTIVATE) e nel body ci sono `username` e `password`, si fa **UPDATE** sul record esistente (nessun nuovo insert) e **200** con `reactivatedFromIncompleteRegistration: true` e `passwordOk: \"new\"`. Fallback su vincolo unico DB come sopra.\n\n**Contesto:** I **Clienti** sono gli iscritti al programma fedeltà: anagrafica, tier, carte, saldi wallet, consensi, promo vinte e storico movimenti.\n\n**Autenticazione:** endpoint pubblico — non richiede `Authorization: Bearer` (può richiedere `X-API-Key` e/o OTP a seconda del caso).\n\n**Body JSON:**\n- `programId` (integer) — obbligatorio\n- `firstName` (string) — opzionale\n- `lastName` (string) — opzionale\n- `email` (string) — opzionale\n- `phone` (string) — opzionale\n- `cardNumber` (string) — opzionale: Fallback manual card number used only if no whitelist is marked assignToNewCustomers.\n- `username` (string) — opzionale\n- `password` (string) — opzionale\n- `otp_code` (string) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Cliente già esistente (stesso email/telefono) senza password: credenziali impostate e accesso attivato (`ok: true`, `passwordOk: \"new\"`; opz. `reactivatedFromIncompleteRegistration: true` se era stub NON_ACTIVATE).\n- **201**: Cliente creato via API pubblica + OTP.\n\n**Errori documentati:** 400, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Customers.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["programId","otp_code"],"properties":{"programId":{"type":"integer"},"firstName":{"type":"string"},"lastName":{"type":"string"},"email":{"type":"string","format":"email"},"phone":{"type":"string"},"cardNumber":{"type":"string","description":"Fallback manual card number used only if no whitelist is marked assignToNewCustomers."},"username":{"type":"string"},"password":{"type":"string","format":"password"},"otp_code":{"type":"string"}}}}},"required":true},"security":[{"apiKey":[]}],"responses":{"200":{"description":"Cliente già esistente (stesso email/telefono) senza password: credenziali impostate e accesso attivato (`ok: true`, `passwordOk: \"new\"`; opz. `reactivatedFromIncompleteRegistration: true` se era stub NON_ACTIVATE).","content":{"application/json":{"schema":{"description":"Cliente già esistente (stesso email/telefono) senza password: credenziali impostate e accesso attivato (`ok: true`, `passwordOk: \"new\"`; opz. `reactivatedFromIncompleteRegistration: true` se era stub NON_ACTIVATE).","type":"object","additionalProperties":true,"example":{"id":42,"firstName":"Luigi","lastName":"Verdi","email":"luigi@example.com","phone":"+393339998877","cardNumber":"900001","customerToken":"Z9Y8X7","username":"luigi.verdi","accessStatus":"ACTIVATE","status":"ACTIVATE","programId":1,"tierId":null,"tierName":null,"cards":[{"id":99,"cardNumber":"900001","tierId":null,"tierName":null,"isPrimary":true,"status":"ACTIVE","createdAt":"2026-04-02T13:48:01.237Z","updatedAt":"2026-04-02T13:48:01.237Z","wallets":[]}],"createdAt":"2026-04-02T13:48:01.237Z","updatedAt":"2026-04-02T13:48:01.237Z","ok":true,"passwordOk":"new","reactivatedFromIncompleteRegistration":true}}}}},"201":{"description":"Cliente creato via API pubblica + OTP.","content":{"application/json":{"schema":{"description":"Cliente creato via API pubblica + OTP.","type":"object","additionalProperties":true,"example":{"id":42,"firstName":"Luigi","lastName":"Verdi","email":"luigi@example.com","phone":"+393339998877","cardNumber":"900001","customerToken":"Z9Y8X7","username":"luigi.verdi","accessStatus":"ACTIVATE","status":"ACTIVATE","programId":1,"tierId":null,"tierName":null,"cards":[{"id":99,"cardNumber":"900001","tierId":null,"tierName":null,"isPrimary":true,"status":"ACTIVE","createdAt":"2026-04-02T13:48:01.237Z","updatedAt":"2026-04-02T13:48:01.237Z","wallets":[]}],"createdAt":"2026-04-02T13:48:01.237Z","updatedAt":"2026-04-02T13:48:01.237Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"409":{"description":"Cliente già registrato nel programma con password configurata. Se nel body è presente `password`, viene verificata rispetto all'account esistente (`passwordOk`: `\"true\"` o `\"false\"`).","content":{"application/json":{"schema":{"description":"Cliente già registrato nel programma con password configurata. Se nel body è presente `password`, viene verificata rispetto all'account esistente (`passwordOk`: `\"true\"` o `\"false\"`).","type":"object","additionalProperties":true,"example":{"error":{"code":"CUSTOMER_ALREADY_EXISTS","message":"Customer already registered for this program"},"passwordOk":"false"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/customers/b2b/checkVat":{"post":{"tags":["Customers"],"description":"Endpoint pubblico APP B2B (**non** richiede Bearer). Verifica se esiste un cliente con la Partita IVA e se il contatto (email o telefono) è associato. Risponde **200** `{ result: \"OK\" }` e invia un OTP al contatto solo in caso affermativo; **404** `{ result: \"KO\" }` se P.IVA assente o contatto non associato. Richiede `businessRules.appB2b.vatRegistration` attivo, altrimenti errore.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["programId"],"properties":{"programId":{"type":"integer"},"vatNumber":{"type":"string","description":"Partita IVA"},"partitaIva":{"type":"string"},"taxNumber":{"type":"string"},"contact":{"type":"string","description":"Email o telefono"},"email":{"type":"string"},"phone":{"type":"string"},"mobile":{"type":"string"}}}}},"required":true},"security":[{"apiKey":[]}],"responses":{"200":{"description":"Cliente con P.IVA trovato e contatto associato; OTP inviato.","content":{"application/json":{"schema":{"description":"Cliente con P.IVA trovato e contatto associato; OTP inviato.","type":"object","additionalProperties":true,"example":{"result":"OK"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"404":{"description":"P.IVA non trovata o contatto non associato al cliente.","content":{"application/json":{"schema":{"description":"P.IVA non trovata o contatto non associato al cliente.","type":"object","additionalProperties":true,"example":{"result":"KO"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/customers/b2b/activateOrCreate":{"post":{"tags":["Customers"],"description":"Endpoint pubblico APP B2B (**non** richiede Bearer). Con OTP valido attiva l'account padre (se family cards è attiva) o il cliente (se family cards è disattiva). Senza OTP valido: se family cards è attiva crea un cliente figlio con padre = cliente P.IVA (creato se mancante); altrimenti errore. Richiede `businessRules.appB2b.vatRegistration` attivo.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["programId"],"properties":{"programId":{"type":"integer"},"vatNumber":{"type":"string","description":"Partita IVA"},"partitaIva":{"type":"string"},"taxNumber":{"type":"string"},"otpCode":{"type":"string"},"otp_code":{"type":"string"},"contact":{"type":"string","description":"Email o telefono del figlio (richiesto in creazione figlio)"},"email":{"type":"string"},"phone":{"type":"string"},"mobile":{"type":"string"},"firstName":{"type":"string"},"lastName":{"type":"string"},"username":{"type":"string"},"password":{"type":"string","format":"password"},"cardNumber":{"type":"string"}}}}},"required":true},"security":[{"apiKey":[]}],"responses":{"200":{"description":"Account padre attivato (family cards on) o cliente attivato (family cards off) dopo OTP valido.","content":{"application/json":{"schema":{"description":"Account padre attivato (family cards on) o cliente attivato (family cards off) dopo OTP valido.","type":"object","additionalProperties":true,"example":{"result":"OK","action":"PARENT_ACTIVATED","id":2,"firstName":"Maria","lastName":"Bianchi","email":"maria.bianchi@example.com","phone":"+393331112233","taxNumber":"01234567890","status":"ACTIVATE","accessStatus":"ACTIVATE","parentCustomerId":null,"programId":1,"cardNumber":"10000001","customerToken":"K7Q2M9","cards":[]}}}}},"201":{"description":"Cliente figlio creato (family cards on) con padre = cliente P.IVA.","content":{"application/json":{"schema":{"description":"Cliente figlio creato (family cards on) con padre = cliente P.IVA.","type":"object","additionalProperties":true,"example":{"result":"OK","action":"CHILD_CREATED","id":88,"firstName":"Luca","lastName":"Neri","email":"luca.neri@example.com","phone":"+393334445566","taxNumber":null,"status":"ACTIVATE","accessStatus":"ACTIVATE","parentCustomerId":2,"parentCreated":false,"programId":1,"cardNumber":"10000088","customerToken":"D4E5F6","cards":[]}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/customers":{"get":{"tags":["Customers"],"description":"Elenco paginato di cliente.\n\n**Contesto:** I **Clienti** sono gli iscritti al programma fedeltà: anagrafica, tier, carte, saldi wallet, consensi, promo vinte e storico movimenti.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Querystring:**\n- `programId` (integer) — obbligatorio\n- `page` (integer) — opzionale [default: 1]\n- `size` (integer) — opzionale [default: 20]\n- `name_surname` (string) — opzionale: Cerca per nome/cognome (testo intero o ultima parola=cognome, restanti=nome)\n- `phone` (string) — opzionale: Cerca con LIKE su phone\n- `card` (string) — opzionale: Cerca esatta su card_number\n- `email` (string) — opzionale: Cerca esatta su email\n- `identityCard` (string) — opzionale: Cerca esatta su identity_card\n- `foreignId` (string) — opzionale: Cerca esatta su foreign_id\n- `birthdate` (string) — opzionale: Data di nascita esatta (YYYY-MM-DD)\n- `status` (integer) — opzionale: 1=ACTIVATE, 2=BLOCKED, 3=DELETED (valori: 1, 2, 3)\n- `activationDateFrom` (string) — opzionale: Filtra clienti attivati da questa data (inclusa)\n- `activationDateTo` (string) — opzionale: Filtra clienti attivati fino a questa data (inclusa)\n- `activationShop` (integer) — opzionale: Filtra per shop di registrazione (registration_shop_id)\n- `giftCardFilter` (string) — opzionale: with_gift_cards = almeno una gift card acquistata/ricevuta; anonymous_gift_cards = clienti placeholder emissione GC POS; no_gift_cards = solo clienti senza gift card (valori: with_gift_cards, anonymous_gift_cards, no_gift_cards)\n\n**Risposte di successo:**\n- **200**: Lista clienti paginata con totali per stato.\n\n**Errori documentati:** 400, 401, 403, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Customers.","parameters":[{"schema":{"type":"integer"},"in":"query","name":"programId","required":true},{"schema":{"type":"integer","default":1},"in":"query","name":"page","required":false},{"schema":{"type":"integer","default":20},"in":"query","name":"size","required":false},{"schema":{"type":"string"},"in":"query","name":"name_surname","required":false,"description":"Cerca per nome/cognome (testo intero o ultima parola=cognome, restanti=nome)"},{"schema":{"type":"string"},"in":"query","name":"phone","required":false,"description":"Cerca con LIKE su phone"},{"schema":{"type":"string"},"in":"query","name":"card","required":false,"description":"Cerca esatta su card_number o foreign_code. Se businessRules.nfcTags.enabled, cerca anche Tag NFC (nfc_uid), match parziale case-insensitive"},{"schema":{"type":"string"},"in":"query","name":"email","required":false,"description":"Cerca esatta su email"},{"schema":{"type":"string"},"in":"query","name":"identityCard","required":false,"description":"Cerca esatta su identity_card"},{"schema":{"type":"string"},"in":"query","name":"taxNumber","required":false,"description":"Cerca esatta su tax_number (Partita IVA)"},{"schema":{"type":"string"},"in":"query","name":"foreignId","required":false,"description":"Cerca esatta su foreign_id"},{"schema":{"type":"string","format":"date"},"in":"query","name":"birthdate","required":false,"description":"Data di nascita esatta (YYYY-MM-DD)"},{"schema":{"type":"integer","enum":[1,2,3]},"in":"query","name":"status","required":false,"description":"1=ACTIVATE, 2=BLOCKED, 3=DELETED"},{"schema":{"type":"string","format":"date"},"in":"query","name":"activationDateFrom","required":false,"description":"Filtra clienti attivati da questa data (inclusa)"},{"schema":{"type":"string","format":"date"},"in":"query","name":"activationDateTo","required":false,"description":"Filtra clienti attivati fino a questa data (inclusa)"},{"schema":{"type":"integer"},"in":"query","name":"activationShop","required":false,"description":"Filtra per shop di registrazione (registration_shop_id)"},{"schema":{"type":"number"},"in":"query","name":"balanceFrom","required":false,"description":"Saldo wallet aggregato minimo (incluso)"},{"schema":{"type":"number"},"in":"query","name":"balanceTo","required":false,"description":"Saldo wallet aggregato massimo (incluso)"},{"schema":{"type":"string","enum":["with_gift_cards","anonymous_gift_cards","no_gift_cards"]},"in":"query","name":"giftCardFilter","required":false,"description":"with_gift_cards = almeno una gift card acquistata/ricevuta; anonymous_gift_cards = clienti placeholder emissione GC POS; no_gift_cards = solo clienti senza gift card"},{"schema":{"type":"integer"},"in":"query","name":"tierId","required":false,"description":"Filtra clienti la cui carta primaria appartiene a questo tier"}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Lista clienti paginata con totali per stato.","content":{"application/json":{"schema":{"description":"Lista clienti paginata con totali per stato.","type":"object","additionalProperties":true,"example":{"content":[{"id":2,"foreignId":"CRM-0002","firstName":"Maria","lastName":"Bianchi","email":"maria.bianchi@example.com","phone":"+393331112233","cardNumber":"10000001","customerToken":"K7Q2M9","status":"ACTIVATE","programId":1,"avatarUrl":"https://cdn.example.com/1/Avatars/2-a1b2c3d4.jpg","tierId":5,"tierName":"Gold","wallets":[{"id":1,"name":"Punti","symbol":"PT","decimalPlaces":2,"useCardContainers":false,"balance":7826.08,"chargedTotal":1378708.08,"dischargedTotal":1370882,"level":"Fuoriclasse","chargeCount":25256,"dischargeCount":6,"lastTransactionAt":"2026-04-02T13:48:01.237Z"},{"id":2,"name":"Status","symbol":"ST","decimalPlaces":0,"useCardContainers":false,"balance":24954,"chargedTotal":24954,"dischargedTotal":0,"level":null,"chargeCount":12474,"dischargeCount":0,"lastTransactionAt":"2026-03-17T16:34:59.878Z"}],"cards":[{"id":1,"cardNumber":"10000001","foreignCode":"CARD-EXT-001","tierId":5,"tierName":"Gold","isPrimary":true,"status":"ACTIVE","createdAt":"2026-03-13T09:35:16.033Z","updatedAt":"2026-03-13T09:35:16.033Z","wallets":[{"walletId":1,"symbol":"PT","name":"Punti","decimalPlaces":2,"useCardContainers":false,"balance":7826.08,"chargedTotal":1378708.08,"dischargedTotal":1370882,"lastTransactionAt":"2026-04-02T13:48:01.237Z"},{"walletId":2,"symbol":"ST","name":"Status","decimalPlaces":0,"useCardContainers":false,"balance":24954,"chargedTotal":24954,"dischargedTotal":0,"lastTransactionAt":"2026-03-17T16:34:59.878Z"}]},{"id":4,"cardNumber":"10000002","foreignCode":null,"tierId":6,"tierName":"Argento","isPrimary":false,"status":"ACTIVE","createdAt":"2026-03-13T10:38:31.289Z","updatedAt":"2026-03-13T10:38:31.289Z","wallets":[]}],"lastUse":"2026-04-01T13:48:44.852Z","createdAt":"2026-02-20T08:26:04.781Z","updatedAt":"2026-04-02T13:45:27.114Z"}],"totalElements":1,"totalPages":1,"page":1,"size":20,"totals":{"customers":1,"stato":[{"id":"ACTIVATE","nome":"Attivo","qtà":1}]}}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"post":{"tags":["Customers"],"description":"Crea cliente.\n\n**Contesto:** I **Clienti** sono gli iscritti al programma fedeltà: anagrafica, tier, carte, saldi wallet, consensi, promo vinte e storico movimenti.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Body JSON:**\n- `programId` (integer) — obbligatorio\n- `firstName` (string) — opzionale\n- `lastName` (string) — opzionale\n- `email` (string) — opzionale\n- `phone` (string) — opzionale\n- `cardNumber` (string) — opzionale\n- `assignCard` (boolean) — opzionale: Se true e cardNumber non è valorizzato, assegna automaticamente una card da whitelist (assign_to_new_customers). [default: false]\n- `foreignId` (string) — opzionale\n- `gender` (string) — opzionale (valori: M, F, X, N, )\n- `birthdate` (string) — opzionale\n- `identityCard` (string) — opzionale\n- `identityCardTypeId` (integer) — opzionale\n- `addressId` (integer) — opzionale\n- `registrationShopId` (integer) — opzionale\n- `registrationNetId` (integer) — opzionale\n\n**Risposte di successo:**\n- **201**: Cliente creato; stesso formato del GET dettaglio.\n\n**Errori documentati:** 400, 401, 403, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Customers.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["programId"],"properties":{"programId":{"type":"integer"},"firstName":{"type":"string"},"lastName":{"type":"string"},"email":{"type":"string","format":"email"},"phone":{"type":"string"},"cardNumber":{"type":"string"},"assignCard":{"type":"boolean","default":false,"description":"Se true e cardNumber non è valorizzato, assegna automaticamente una card da whitelist (assign_to_new_customers)."},"foreignId":{"type":"string","nullable":true},"notes":{"type":"string","nullable":true},"gender":{"type":"string","enum":["M","F","X","N",null],"nullable":true},"birthdate":{"type":"string","format":"date","nullable":true},"identityCard":{"type":"string","nullable":true},"identityCardTypeId":{"type":"integer","nullable":true},"taxNumber":{"type":"string","description":"Partita IVA","nullable":true},"addressId":{"type":"integer","nullable":true},"registrationShopId":{"type":"integer","nullable":true},"registrationNetId":{"type":"integer","nullable":true},"phoneOtp":{"type":"string","description":"OTP SMS del cellulare. Obbligatorio in creazione da POS se il cellulare è passato o è obbligatorio in anagrafica."}}}}},"required":true},"security":[{"bearerAuth":[]}],"responses":{"201":{"description":"Cliente creato; stesso formato del GET dettaglio.","content":{"application/json":{"schema":{"description":"Cliente creato; stesso formato del GET dettaglio.","type":"object","additionalProperties":true,"example":{"id":2,"foreignId":"CRM-0002","firstName":"Maria","lastName":"Bianchi","email":"maria.bianchi@example.com","phone":"+393331112233","notes":"Cliente VIP — preferisce contatto telefonico","cardNumber":"10000001","customerToken":"K7Q2M9","status":"ACTIVATE","programId":1,"avatarUrl":"https://cdn.example.com/1/Avatars/2-a1b2c3d4.jpg","tierId":5,"tierName":"Gold","cards":[{"id":1,"cardNumber":"10000001","foreignCode":"CARD-EXT-001","tierId":5,"tierName":"Gold","isPrimary":true,"status":"ACTIVE","createdAt":"2026-03-13T09:35:16.033Z","updatedAt":"2026-03-13T09:35:16.033Z","wallets":[{"walletId":1,"symbol":"PT","name":"Punti","decimalPlaces":2,"useCardContainers":false,"balance":7826.08,"chargedTotal":1378708.08,"dischargedTotal":1370882,"lastTransactionAt":"2026-04-02T13:48:01.237Z"},{"walletId":2,"symbol":"ST","name":"Status","decimalPlaces":0,"useCardContainers":false,"balance":24954,"chargedTotal":24954,"dischargedTotal":0,"lastTransactionAt":"2026-03-17T16:34:59.878Z"}]},{"id":4,"cardNumber":"10000002","foreignCode":null,"tierId":6,"tierName":"Argento","isPrimary":false,"status":"ACTIVE","createdAt":"2026-03-13T10:38:31.289Z","updatedAt":"2026-03-13T10:38:31.289Z","wallets":[]}],"gender":"F","birthdate":"1985-05-15T00:00:00.000Z","identityCard":"BNCMRA85E55Z600X","identityCardTypeId":null,"taxNumber":"01234567890","registrationShopId":null,"registrationNetId":null,"addressId":3,"address":{"id":3,"street":"Via Roma","number":"10","floor":"","apartment":"","zipCode":"50100","city":"Firenze","state":"FI","country":"IT","latitude":43.7696,"longitude":11.2558},"wallets":[{"id":1,"name":"Punti","symbol":"PT","decimalPlaces":2,"useCardContainers":false,"balance":7826.08,"chargedTotal":1378708.08,"dischargedTotal":1370882,"level":"Fuoriclasse","chargeCount":25256,"dischargeCount":6,"lastTransactionAt":"2026-04-02T13:48:01.237Z"},{"id":2,"name":"Status","symbol":"ST","decimalPlaces":0,"useCardContainers":false,"balance":24954,"chargedTotal":24954,"dischargedTotal":0,"level":null,"chargeCount":12474,"dischargeCount":0,"lastTransactionAt":"2026-03-17T16:34:59.878Z"}],"tags":[{"id":6,"name":"VIP","description":"Cliente VIP","tagType":"customers","color":"green","iconPath":"","assignedAt":"2026-03-31T19:06:17.238Z"}],"vouchers":{"total":0,"active":0},"dynamicFields":[{"dynamicFieldId":1,"dynamicFieldName":"Colore preferito","value":"blue","updatedAt":"2026-04-02T13:45:27.173Z"}],"consents":[{"consentDefinitionId":1,"name":"Privacy","description":"Consenso alla Privacy","required":false,"expiryMonths":24,"granted":false,"grantedAt":null,"expiresAt":null},{"consentDefinitionId":2,"name":"Marketing","description":"Consenso al Marketing","required":false,"expiryMonths":0,"granted":false,"grantedAt":"2026-04-02T14:53:51.457Z","expiresAt":"2026-04-02T14:53:51.457Z"}],"parentCustomerId":10,"familyCanDischargeOverride":false,"familyParent":{"id":10,"firstName":"Giovanni","lastName":"Bianchi","email":"giovanni.bianchi@example.com","phone":"+393334445566","status":"ACTIVATE","cardNumber":"10000000","customerToken":"A1B2C3"},"familyChildren":[{"id":3,"firstName":"Luca","lastName":"Bianchi","email":"luca.bianchi@example.com","phone":null,"status":"ACTIVATE","cardNumber":"10000003","customerToken":"D4E5F6","familyCanDischargeOverride":true}],"preferredShops":[],"revenueTiers":{"previousRevenue":12000,"currentRevenue":9800,"achievedPercent":81.667,"currentFromDate":"2026-01-01","mode":"previous_revenue","previousCalculatedValueId":12,"currentLevelPercentage":80,"currentLevelTierId":5,"levels":[{"percentage":50,"tierId":4,"tierName":"Silver","reached":true},{"percentage":80,"tierId":5,"tierName":"Gold","reached":true},{"percentage":100,"tierId":6,"tierName":"Platinum","reached":false}]},"lastUse":"2026-04-01T13:48:44.852Z","createdAt":"2026-02-20T08:26:04.781Z","updatedAt":"2026-04-02T13:45:27.114Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/customers/me":{"get":{"tags":["Customers"],"description":"Get authenticated customer details (same payload as GET /customers/:id). Requires Bearer JWT with operatorType CUSTOMER; customer id is taken from token `sub`. I wallet rispettano `tier_wallet_access` come allowlist: se il wallet ha righe, resta visibile solo con card del cliente il cui tier ha `can_view`. Senza righe, default permissivo. I wallet `useCardContainers` restano solo su `cards[].wallets` (non nell'aggregato top-level).","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Dettaglio cliente (wallet/card filtrati per JWT CUSTOMER se applicabile).","content":{"application/json":{"schema":{"description":"Dettaglio cliente (wallet/card filtrati per JWT CUSTOMER se applicabile).","type":"object","additionalProperties":true,"example":{"id":2,"foreignId":"CRM-0002","firstName":"Maria","lastName":"Bianchi","email":"maria.bianchi@example.com","phone":"+393331112233","notes":"Cliente VIP — preferisce contatto telefonico","cardNumber":"10000001","customerToken":"K7Q2M9","status":"ACTIVATE","programId":1,"avatarUrl":"https://cdn.example.com/1/Avatars/2-a1b2c3d4.jpg","tierId":5,"tierName":"Gold","cards":[{"id":1,"cardNumber":"10000001","foreignCode":"CARD-EXT-001","tierId":5,"tierName":"Gold","isPrimary":true,"status":"ACTIVE","createdAt":"2026-03-13T09:35:16.033Z","updatedAt":"2026-03-13T09:35:16.033Z","wallets":[{"walletId":1,"symbol":"PT","name":"Punti","decimalPlaces":2,"useCardContainers":false,"balance":7826.08,"chargedTotal":1378708.08,"dischargedTotal":1370882,"lastTransactionAt":"2026-04-02T13:48:01.237Z"},{"walletId":2,"symbol":"ST","name":"Status","decimalPlaces":0,"useCardContainers":false,"balance":24954,"chargedTotal":24954,"dischargedTotal":0,"lastTransactionAt":"2026-03-17T16:34:59.878Z"}]},{"id":4,"cardNumber":"10000002","foreignCode":null,"tierId":6,"tierName":"Argento","isPrimary":false,"status":"ACTIVE","createdAt":"2026-03-13T10:38:31.289Z","updatedAt":"2026-03-13T10:38:31.289Z","wallets":[]}],"gender":"F","birthdate":"1985-05-15T00:00:00.000Z","identityCard":"BNCMRA85E55Z600X","identityCardTypeId":null,"taxNumber":"01234567890","registrationShopId":null,"registrationNetId":null,"addressId":3,"address":{"id":3,"street":"Via Roma","number":"10","floor":"","apartment":"","zipCode":"50100","city":"Firenze","state":"FI","country":"IT","latitude":43.7696,"longitude":11.2558},"wallets":[{"id":1,"name":"Punti","symbol":"PT","decimalPlaces":2,"useCardContainers":false,"balance":7826.08,"chargedTotal":1378708.08,"dischargedTotal":1370882,"level":"Fuoriclasse","chargeCount":25256,"dischargeCount":6,"lastTransactionAt":"2026-04-02T13:48:01.237Z"},{"id":2,"name":"Status","symbol":"ST","decimalPlaces":0,"useCardContainers":false,"balance":24954,"chargedTotal":24954,"dischargedTotal":0,"level":null,"chargeCount":12474,"dischargeCount":0,"lastTransactionAt":"2026-03-17T16:34:59.878Z"}],"tags":[{"id":6,"name":"VIP","description":"Cliente VIP","tagType":"customers","color":"green","iconPath":"","assignedAt":"2026-03-31T19:06:17.238Z"}],"vouchers":{"total":0,"active":0},"dynamicFields":[{"dynamicFieldId":1,"dynamicFieldName":"Colore preferito","value":"blue","updatedAt":"2026-04-02T13:45:27.173Z"}],"consents":[{"consentDefinitionId":1,"name":"Privacy","description":"Consenso alla Privacy","required":false,"expiryMonths":24,"granted":false,"grantedAt":null,"expiresAt":null},{"consentDefinitionId":2,"name":"Marketing","description":"Consenso al Marketing","required":false,"expiryMonths":0,"granted":false,"grantedAt":"2026-04-02T14:53:51.457Z","expiresAt":"2026-04-02T14:53:51.457Z"}],"parentCustomerId":10,"familyCanDischargeOverride":false,"familyParent":{"id":10,"firstName":"Giovanni","lastName":"Bianchi","email":"giovanni.bianchi@example.com","phone":"+393334445566","status":"ACTIVATE","cardNumber":"10000000","customerToken":"A1B2C3"},"familyChildren":[{"id":3,"firstName":"Luca","lastName":"Bianchi","email":"luca.bianchi@example.com","phone":null,"status":"ACTIVATE","cardNumber":"10000003","customerToken":"D4E5F6","familyCanDischargeOverride":true}],"preferredShops":[],"revenueTiers":{"previousRevenue":12000,"currentRevenue":9800,"achievedPercent":81.667,"currentFromDate":"2026-01-01","mode":"previous_revenue","previousCalculatedValueId":12,"currentLevelPercentage":80,"currentLevelTierId":5,"levels":[{"percentage":50,"tierId":4,"tierName":"Silver","reached":true},{"percentage":80,"tierId":5,"tierName":"Gold","reached":true},{"percentage":100,"tierId":6,"tierName":"Platinum","reached":false}]},"lastUse":"2026-04-01T13:48:44.852Z","createdAt":"2026-02-20T08:26:04.781Z","updatedAt":"2026-04-02T13:45:27.114Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/customers/{id}":{"get":{"tags":["Customers"],"description":"Get customer details. I wallet rispettano `tier_wallet_access` come allowlist: se il wallet ha righe, resta visibile solo con card del cliente il cui tier ha `can_view` (CUSTOMER, Admin e POS). Senza righe, default permissivo. Con JWT `CUSTOMER`, i wallet `useCardContainers` restano solo su `cards[].wallets`.\n\n**Contesto:** I **Clienti** sono gli iscritti al programma fedeltà: anagrafica, tier, carte, saldi wallet, consensi, promo vinte e storico movimenti.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `id` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Dettaglio cliente (wallet filtrati per allowlist tier delle card).\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Customers.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Dettaglio cliente (wallet/card filtrati per JWT CUSTOMER se applicabile).","content":{"application/json":{"schema":{"description":"Dettaglio cliente (wallet/card filtrati per JWT CUSTOMER se applicabile).","type":"object","additionalProperties":true,"example":{"id":2,"foreignId":"CRM-0002","firstName":"Maria","lastName":"Bianchi","email":"maria.bianchi@example.com","phone":"+393331112233","notes":"Cliente VIP — preferisce contatto telefonico","cardNumber":"10000001","customerToken":"K7Q2M9","status":"ACTIVATE","programId":1,"avatarUrl":"https://cdn.example.com/1/Avatars/2-a1b2c3d4.jpg","tierId":5,"tierName":"Gold","cards":[{"id":1,"cardNumber":"10000001","foreignCode":"CARD-EXT-001","tierId":5,"tierName":"Gold","isPrimary":true,"status":"ACTIVE","createdAt":"2026-03-13T09:35:16.033Z","updatedAt":"2026-03-13T09:35:16.033Z","wallets":[{"walletId":1,"symbol":"PT","name":"Punti","decimalPlaces":2,"useCardContainers":false,"balance":7826.08,"chargedTotal":1378708.08,"dischargedTotal":1370882,"lastTransactionAt":"2026-04-02T13:48:01.237Z"},{"walletId":2,"symbol":"ST","name":"Status","decimalPlaces":0,"useCardContainers":false,"balance":24954,"chargedTotal":24954,"dischargedTotal":0,"lastTransactionAt":"2026-03-17T16:34:59.878Z"}]},{"id":4,"cardNumber":"10000002","foreignCode":null,"tierId":6,"tierName":"Argento","isPrimary":false,"status":"ACTIVE","createdAt":"2026-03-13T10:38:31.289Z","updatedAt":"2026-03-13T10:38:31.289Z","wallets":[]}],"gender":"F","birthdate":"1985-05-15T00:00:00.000Z","identityCard":"BNCMRA85E55Z600X","identityCardTypeId":null,"taxNumber":"01234567890","registrationShopId":null,"registrationNetId":null,"addressId":3,"address":{"id":3,"street":"Via Roma","number":"10","floor":"","apartment":"","zipCode":"50100","city":"Firenze","state":"FI","country":"IT","latitude":43.7696,"longitude":11.2558},"wallets":[{"id":1,"name":"Punti","symbol":"PT","decimalPlaces":2,"useCardContainers":false,"balance":7826.08,"chargedTotal":1378708.08,"dischargedTotal":1370882,"level":"Fuoriclasse","chargeCount":25256,"dischargeCount":6,"lastTransactionAt":"2026-04-02T13:48:01.237Z"},{"id":2,"name":"Status","symbol":"ST","decimalPlaces":0,"useCardContainers":false,"balance":24954,"chargedTotal":24954,"dischargedTotal":0,"level":null,"chargeCount":12474,"dischargeCount":0,"lastTransactionAt":"2026-03-17T16:34:59.878Z"}],"tags":[{"id":6,"name":"VIP","description":"Cliente VIP","tagType":"customers","color":"green","iconPath":"","assignedAt":"2026-03-31T19:06:17.238Z"}],"vouchers":{"total":0,"active":0},"dynamicFields":[{"dynamicFieldId":1,"dynamicFieldName":"Colore preferito","value":"blue","updatedAt":"2026-04-02T13:45:27.173Z"}],"consents":[{"consentDefinitionId":1,"name":"Privacy","description":"Consenso alla Privacy","required":false,"expiryMonths":24,"granted":false,"grantedAt":null,"expiresAt":null},{"consentDefinitionId":2,"name":"Marketing","description":"Consenso al Marketing","required":false,"expiryMonths":0,"granted":false,"grantedAt":"2026-04-02T14:53:51.457Z","expiresAt":"2026-04-02T14:53:51.457Z"}],"parentCustomerId":10,"familyCanDischargeOverride":false,"familyParent":{"id":10,"firstName":"Giovanni","lastName":"Bianchi","email":"giovanni.bianchi@example.com","phone":"+393334445566","status":"ACTIVATE","cardNumber":"10000000","customerToken":"A1B2C3"},"familyChildren":[{"id":3,"firstName":"Luca","lastName":"Bianchi","email":"luca.bianchi@example.com","phone":null,"status":"ACTIVATE","cardNumber":"10000003","customerToken":"D4E5F6","familyCanDischargeOverride":true}],"preferredShops":[],"revenueTiers":{"previousRevenue":12000,"currentRevenue":9800,"achievedPercent":81.667,"currentFromDate":"2026-01-01","mode":"previous_revenue","previousCalculatedValueId":12,"currentLevelPercentage":80,"currentLevelTierId":5,"levels":[{"percentage":50,"tierId":4,"tierName":"Silver","reached":true},{"percentage":80,"tierId":5,"tierName":"Gold","reached":true},{"percentage":100,"tierId":6,"tierName":"Platinum","reached":false}]},"lastUse":"2026-04-01T13:48:44.852Z","createdAt":"2026-02-20T08:26:04.781Z","updatedAt":"2026-04-02T13:45:27.114Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"delete":{"tags":["Customers"],"description":"Hard-delete customer. Fails if the customer has movements or other references; in that case use POST /api/v1/customers/purge.\n\n**Contesto:** I **Clienti** sono gli iscritti al programma fedeltà: anagrafica, tier, carte, saldi wallet, consensi, promo vinte e storico movimenti.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `id` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Eliminazione hard (solo senza riferimenti).\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Customers.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Eliminazione hard (solo senza riferimenti).","content":{"application/json":{"schema":{"description":"Eliminazione hard (solo senza riferimenti).","type":"object","additionalProperties":true,"example":{"deleted":true,"customerId":2}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"put":{"tags":["Customers"],"description":"Update customer. programId, tierId, cardNumber and status cannot be changed here. foreignId can be changed only by non-CUSTOMER tokens.\n\n**Contesto:** I **Clienti** sono gli iscritti al programma fedeltà: anagrafica, tier, carte, saldi wallet, consensi, promo vinte e storico movimenti.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `id` (integer) — obbligatorio\n\n**Body JSON:**\n- `firstName` (string) — opzionale\n- `lastName` (string) — opzionale\n- `email` (string) — opzionale\n- `phone` (string) — opzionale\n- `foreignId` (string) — opzionale\n- `addressId` (integer) — opzionale\n- `identityCard` (string) — opzionale\n- `identityCardTypeId` (integer) — opzionale\n- `birthdate` (string) — opzionale\n- `gender` (string) — opzionale (valori: M, F, X, N, )\n\n**Risposte di successo:**\n- **200**: Dettaglio cliente (wallet/card filtrati per JWT CUSTOMER se applicabile).\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Customers.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"firstName":{"type":"string"},"lastName":{"type":"string"},"email":{"type":"string","format":"email"},"phone":{"type":"string"},"foreignId":{"type":"string","nullable":true},"notes":{"type":"string","nullable":true},"addressId":{"type":"integer","nullable":true},"identityCard":{"type":"string","nullable":true},"identityCardTypeId":{"type":"integer","nullable":true},"taxNumber":{"type":"string","description":"Partita IVA","nullable":true},"birthdate":{"type":"string","format":"date","nullable":true},"gender":{"type":"string","enum":["M","F","X","N",null],"nullable":true}}}}}},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Dettaglio cliente (wallet/card filtrati per JWT CUSTOMER se applicabile).","content":{"application/json":{"schema":{"description":"Dettaglio cliente (wallet/card filtrati per JWT CUSTOMER se applicabile).","type":"object","additionalProperties":true,"example":{"id":2,"foreignId":"CRM-0002","firstName":"Maria","lastName":"Bianchi","email":"maria.bianchi@example.com","phone":"+393331112233","notes":"Cliente VIP — preferisce contatto telefonico","cardNumber":"10000001","customerToken":"K7Q2M9","status":"ACTIVATE","programId":1,"avatarUrl":"https://cdn.example.com/1/Avatars/2-a1b2c3d4.jpg","tierId":5,"tierName":"Gold","cards":[{"id":1,"cardNumber":"10000001","foreignCode":"CARD-EXT-001","tierId":5,"tierName":"Gold","isPrimary":true,"status":"ACTIVE","createdAt":"2026-03-13T09:35:16.033Z","updatedAt":"2026-03-13T09:35:16.033Z","wallets":[{"walletId":1,"symbol":"PT","name":"Punti","decimalPlaces":2,"useCardContainers":false,"balance":7826.08,"chargedTotal":1378708.08,"dischargedTotal":1370882,"lastTransactionAt":"2026-04-02T13:48:01.237Z"},{"walletId":2,"symbol":"ST","name":"Status","decimalPlaces":0,"useCardContainers":false,"balance":24954,"chargedTotal":24954,"dischargedTotal":0,"lastTransactionAt":"2026-03-17T16:34:59.878Z"}]},{"id":4,"cardNumber":"10000002","foreignCode":null,"tierId":6,"tierName":"Argento","isPrimary":false,"status":"ACTIVE","createdAt":"2026-03-13T10:38:31.289Z","updatedAt":"2026-03-13T10:38:31.289Z","wallets":[]}],"gender":"F","birthdate":"1985-05-15T00:00:00.000Z","identityCard":"BNCMRA85E55Z600X","identityCardTypeId":null,"taxNumber":"01234567890","registrationShopId":null,"registrationNetId":null,"addressId":3,"address":{"id":3,"street":"Via Roma","number":"10","floor":"","apartment":"","zipCode":"50100","city":"Firenze","state":"FI","country":"IT","latitude":43.7696,"longitude":11.2558},"wallets":[{"id":1,"name":"Punti","symbol":"PT","decimalPlaces":2,"useCardContainers":false,"balance":7826.08,"chargedTotal":1378708.08,"dischargedTotal":1370882,"level":"Fuoriclasse","chargeCount":25256,"dischargeCount":6,"lastTransactionAt":"2026-04-02T13:48:01.237Z"},{"id":2,"name":"Status","symbol":"ST","decimalPlaces":0,"useCardContainers":false,"balance":24954,"chargedTotal":24954,"dischargedTotal":0,"level":null,"chargeCount":12474,"dischargeCount":0,"lastTransactionAt":"2026-03-17T16:34:59.878Z"}],"tags":[{"id":6,"name":"VIP","description":"Cliente VIP","tagType":"customers","color":"green","iconPath":"","assignedAt":"2026-03-31T19:06:17.238Z"}],"vouchers":{"total":0,"active":0},"dynamicFields":[{"dynamicFieldId":1,"dynamicFieldName":"Colore preferito","value":"blue","updatedAt":"2026-04-02T13:45:27.173Z"}],"consents":[{"consentDefinitionId":1,"name":"Privacy","description":"Consenso alla Privacy","required":false,"expiryMonths":24,"granted":false,"grantedAt":null,"expiresAt":null},{"consentDefinitionId":2,"name":"Marketing","description":"Consenso al Marketing","required":false,"expiryMonths":0,"granted":false,"grantedAt":"2026-04-02T14:53:51.457Z","expiresAt":"2026-04-02T14:53:51.457Z"}],"parentCustomerId":10,"familyCanDischargeOverride":false,"familyParent":{"id":10,"firstName":"Giovanni","lastName":"Bianchi","email":"giovanni.bianchi@example.com","phone":"+393334445566","status":"ACTIVATE","cardNumber":"10000000","customerToken":"A1B2C3"},"familyChildren":[{"id":3,"firstName":"Luca","lastName":"Bianchi","email":"luca.bianchi@example.com","phone":null,"status":"ACTIVATE","cardNumber":"10000003","customerToken":"D4E5F6","familyCanDischargeOverride":true}],"preferredShops":[],"revenueTiers":{"previousRevenue":12000,"currentRevenue":9800,"achievedPercent":81.667,"currentFromDate":"2026-01-01","mode":"previous_revenue","previousCalculatedValueId":12,"currentLevelPercentage":80,"currentLevelTierId":5,"levels":[{"percentage":50,"tierId":4,"tierName":"Silver","reached":true},{"percentage":80,"tierId":5,"tierName":"Gold","reached":true},{"percentage":100,"tierId":6,"tierName":"Platinum","reached":false}]},"lastUse":"2026-04-01T13:48:44.852Z","createdAt":"2026-02-20T08:26:04.781Z","updatedAt":"2026-04-02T13:45:27.114Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/customers/{id}/avatar":{"get":{"tags":["Customers"],"description":"Restituisce `{ url }` dell'avatar del cliente (stesso URL di `avatarUrl` nel dettaglio GET /customers/:id). `url` è `null` se non impostato.\n\n**Contesto:** I **Clienti** sono gli iscritti al programma fedeltà: anagrafica, tier, carte, saldi wallet, consensi, promo vinte e storico movimenti.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `id` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **200**: URL immagine avatar (null se non impostata).\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Customers.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"URL immagine avatar (null se non impostata).","content":{"application/json":{"schema":{"description":"URL immagine avatar (null se non impostata).","type":"object","additionalProperties":true,"example":{"url":"https://cdn.example.com/1/Avatars/2-a1b2c3d4.jpg"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"post":{"tags":["Customers"],"description":"Carica l'avatar del cliente: multipart campo `file` (jpeg, png, webp, gif). Oggetto S3: `{programId}/Avatars/{customerId}-{uuid}.ext`; aggiorna `customers.avatar_url` e rimuove il precedente oggetto sul bucket se riconosciuto.\n\n**Contesto:** I **Clienti** sono gli iscritti al programma fedeltà: anagrafica, tier, carte, saldi wallet, consensi, promo vinte e storico movimenti.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `id` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Dettaglio cliente (wallet/card filtrati per JWT CUSTOMER se applicabile).\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Customers.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Dettaglio cliente (wallet/card filtrati per JWT CUSTOMER se applicabile).","content":{"application/json":{"schema":{"description":"Dettaglio cliente (wallet/card filtrati per JWT CUSTOMER se applicabile).","type":"object","additionalProperties":true,"example":{"id":2,"foreignId":"CRM-0002","firstName":"Maria","lastName":"Bianchi","email":"maria.bianchi@example.com","phone":"+393331112233","notes":"Cliente VIP — preferisce contatto telefonico","cardNumber":"10000001","customerToken":"K7Q2M9","status":"ACTIVATE","programId":1,"avatarUrl":"https://cdn.example.com/1/Avatars/2-a1b2c3d4.jpg","tierId":5,"tierName":"Gold","cards":[{"id":1,"cardNumber":"10000001","foreignCode":"CARD-EXT-001","tierId":5,"tierName":"Gold","isPrimary":true,"status":"ACTIVE","createdAt":"2026-03-13T09:35:16.033Z","updatedAt":"2026-03-13T09:35:16.033Z","wallets":[{"walletId":1,"symbol":"PT","name":"Punti","decimalPlaces":2,"useCardContainers":false,"balance":7826.08,"chargedTotal":1378708.08,"dischargedTotal":1370882,"lastTransactionAt":"2026-04-02T13:48:01.237Z"},{"walletId":2,"symbol":"ST","name":"Status","decimalPlaces":0,"useCardContainers":false,"balance":24954,"chargedTotal":24954,"dischargedTotal":0,"lastTransactionAt":"2026-03-17T16:34:59.878Z"}]},{"id":4,"cardNumber":"10000002","foreignCode":null,"tierId":6,"tierName":"Argento","isPrimary":false,"status":"ACTIVE","createdAt":"2026-03-13T10:38:31.289Z","updatedAt":"2026-03-13T10:38:31.289Z","wallets":[]}],"gender":"F","birthdate":"1985-05-15T00:00:00.000Z","identityCard":"BNCMRA85E55Z600X","identityCardTypeId":null,"taxNumber":"01234567890","registrationShopId":null,"registrationNetId":null,"addressId":3,"address":{"id":3,"street":"Via Roma","number":"10","floor":"","apartment":"","zipCode":"50100","city":"Firenze","state":"FI","country":"IT","latitude":43.7696,"longitude":11.2558},"wallets":[{"id":1,"name":"Punti","symbol":"PT","decimalPlaces":2,"useCardContainers":false,"balance":7826.08,"chargedTotal":1378708.08,"dischargedTotal":1370882,"level":"Fuoriclasse","chargeCount":25256,"dischargeCount":6,"lastTransactionAt":"2026-04-02T13:48:01.237Z"},{"id":2,"name":"Status","symbol":"ST","decimalPlaces":0,"useCardContainers":false,"balance":24954,"chargedTotal":24954,"dischargedTotal":0,"level":null,"chargeCount":12474,"dischargeCount":0,"lastTransactionAt":"2026-03-17T16:34:59.878Z"}],"tags":[{"id":6,"name":"VIP","description":"Cliente VIP","tagType":"customers","color":"green","iconPath":"","assignedAt":"2026-03-31T19:06:17.238Z"}],"vouchers":{"total":0,"active":0},"dynamicFields":[{"dynamicFieldId":1,"dynamicFieldName":"Colore preferito","value":"blue","updatedAt":"2026-04-02T13:45:27.173Z"}],"consents":[{"consentDefinitionId":1,"name":"Privacy","description":"Consenso alla Privacy","required":false,"expiryMonths":24,"granted":false,"grantedAt":null,"expiresAt":null},{"consentDefinitionId":2,"name":"Marketing","description":"Consenso al Marketing","required":false,"expiryMonths":0,"granted":false,"grantedAt":"2026-04-02T14:53:51.457Z","expiresAt":"2026-04-02T14:53:51.457Z"}],"parentCustomerId":10,"familyCanDischargeOverride":false,"familyParent":{"id":10,"firstName":"Giovanni","lastName":"Bianchi","email":"giovanni.bianchi@example.com","phone":"+393334445566","status":"ACTIVATE","cardNumber":"10000000","customerToken":"A1B2C3"},"familyChildren":[{"id":3,"firstName":"Luca","lastName":"Bianchi","email":"luca.bianchi@example.com","phone":null,"status":"ACTIVATE","cardNumber":"10000003","customerToken":"D4E5F6","familyCanDischargeOverride":true}],"preferredShops":[],"revenueTiers":{"previousRevenue":12000,"currentRevenue":9800,"achievedPercent":81.667,"currentFromDate":"2026-01-01","mode":"previous_revenue","previousCalculatedValueId":12,"currentLevelPercentage":80,"currentLevelTierId":5,"levels":[{"percentage":50,"tierId":4,"tierName":"Silver","reached":true},{"percentage":80,"tierId":5,"tierName":"Gold","reached":true},{"percentage":100,"tierId":6,"tierName":"Platinum","reached":false}]},"lastUse":"2026-04-01T13:48:44.852Z","createdAt":"2026-02-20T08:26:04.781Z","updatedAt":"2026-04-02T13:45:27.114Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/customers/{id}/vouchers":{"get":{"tags":["Customers"],"description":"List vouchers of a customer. Status filter: all, pending (PENDING), delivered (DELIVERED=consegnato), available (DELIVERED=disponibile/usabile), used (USED), expired (EXPIRED), cancelled (CANCELLED).\n\n**Contesto:** I **Clienti** sono gli iscritti al programma fedeltà: anagrafica, tier, carte, saldi wallet, consensi, promo vinte e storico movimenti.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `id` (integer) — obbligatorio\n\n**Querystring:**\n- `status` (string) — opzionale: all=tutti, pending=in attesa, delivered=consegnato (assegnato e valido), available=disponibile (come delivered), used=usato, expired=scaduto, cancelled=annullato (valori: all, pending, delivered, available, used, expired, cancelled)\n- `page` (integer) — opzionale [default: 1]\n- `size` (integer) — opzionale [default: 20]\n\n**Risposte di successo:**\n- **200**: Voucher del cliente (campi voucher + valid, invalidReason). Per status USED: usedInShop { id, name } dal movimento di riscatto.\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Customers.","parameters":[{"schema":{"type":"string","enum":["all","pending","delivered","available","used","expired","cancelled"]},"in":"query","name":"status","required":false,"description":"all=tutti, pending=in attesa, delivered=consegnato (assegnato e valido), available=disponibile (come delivered), used=usato, expired=scaduto, cancelled=annullato"},{"schema":{"type":"integer","default":1},"in":"query","name":"page","required":false},{"schema":{"type":"integer","default":20},"in":"query","name":"size","required":false},{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Voucher del cliente (campi voucher + valid, invalidReason). Per status USED: usedInShop { id, name } dal movimento di riscatto.","content":{"application/json":{"schema":{"description":"Voucher del cliente (campi voucher + valid, invalidReason). Per status USED: usedInShop { id, name } dal movimento di riscatto.","type":"object","additionalProperties":true,"example":{"content":[{"id":500,"programId":1,"code":"VOUCH-ABC123","customerId":2,"promotionId":10,"vouchersSetId":3,"status":"DELIVERED","title":"Sconto 10€","description":"Valido su spesa minima 50€","valueType":"FIXED","currencyId":1,"expiresAt":"2026-12-31T23:59:59.000Z","allowedShopIds":null,"allowedShopCategoryIds":null,"minimumAmount":50,"discountValue":10,"discountPercent":0,"discountAppliedAmount":null,"deliveredAt":"2026-04-02T13:48:01.237Z","usedAt":null,"generatedByMovementId":1001,"usedInMovementId":null,"usedInShop":null,"createdAt":"2026-04-02T13:48:01.237Z","extraData":null,"extraDataJson":null,"imageUrl":"https://cdn.example/voucher-lotto.png","voucherSet":null,"valid":true,"invalidReason":null},{"id":500,"programId":1,"code":"VOUCH-ABC123","customerId":2,"promotionId":10,"vouchersSetId":3,"status":"USED","title":"Sconto 10€","description":"Valido su spesa minima 50€","valueType":"FIXED","currencyId":1,"expiresAt":"2026-12-31T23:59:59.000Z","allowedShopIds":null,"allowedShopCategoryIds":null,"minimumAmount":50,"discountValue":10,"discountPercent":0,"discountAppliedAmount":5,"deliveredAt":"2026-04-02T13:48:01.237Z","usedAt":"2026-04-02T13:48:01.237Z","generatedByMovementId":1001,"usedInMovementId":82877,"usedInShop":{"id":2988,"name":"Hippie Chic"},"createdAt":"2026-04-02T13:48:01.237Z","extraData":null,"extraDataJson":null,"imageUrl":"https://cdn.example/voucher-lotto.png","voucherSet":null,"valid":false,"invalidReason":"Voucher already used"}],"totalElements":1,"totalPages":1,"page":1,"size":20}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/customers/{id}/promosCanWin":{"get":{"tags":["Customers"],"description":"Solo clienti **ACTIVATE**; altrimenti `promotions: []`. Include `description`, `imageUrl` / `imageUrls`, `mainPrizes` / `consolationPrizes`. `participationCount` da execution logs; `winCount` da `promotion_winners` (esclusi `prize_type` tipo TAG, COMMUNICATE, MESSAGE, WEBHOOK, …). JWT `CUSTOMER` solo il proprio `id`. Con token **CUSTOMER** non compaiono promo con `notVisibleToCustomer` (backoffice/POS le vedono).\n\n**Contesto:** I **Clienti** sono gli iscritti al programma fedeltà: anagrafica, tier, carte, saldi wallet, consensi, promo vinte e storico movimenti.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `id` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Solo cliente ACTIVATE; altrimenti lista vuota. Per promo: canWin*, participationCount, winCount.\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Customers.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Solo cliente ACTIVATE; altrimenti lista vuota. Per promo: canWin*, participationCount, winCount. Ogni elemento di `mainPrizes` / `consolationPrizes` sintetizza un nodo workflow raggiungibile (ACTION, COMMUNICATE, INSTANT_WIN): campi comuni `nodeId`, `workflowNodeType`, `title`, `subtitle`, `actionType`; WALLET → walletId, walletValue, walletOperation, walletValueType; COUPON → voucherSetId; REWARD/PRIZE → prizeId; COMMUNICATE → channels[]; INSTANT_WIN → quantity, drawMode, uniformByShop, uniformByDuration.","content":{"application/json":{"schema":{"description":"Solo cliente ACTIVATE; altrimenti lista vuota. Per promo: canWin*, participationCount, winCount. Ogni elemento di `mainPrizes` / `consolationPrizes` sintetizza un nodo workflow raggiungibile (ACTION, COMMUNICATE, INSTANT_WIN): campi comuni `nodeId`, `workflowNodeType`, `title`, `subtitle`, `actionType`; WALLET → walletId, walletValue, walletOperation, walletValueType; COUPON → voucherSetId; REWARD/PRIZE → prizeId; COMMUNICATE → channels[]; INSTANT_WIN → quantity, drawMode, uniformByShop, uniformByDuration.","type":"object","additionalProperties":true,"example":{"promotions":[{"id":1,"name":"Doppi Punti Weekend","description":"Carica punti doppi su ogni acquisto nel weekend.","priority":0,"startDate":"2026-02-19T14:00:00.000Z","endDate":"2026-04-30T08:59:00.000Z","imageUrls":["https://cdn.example/promo-doppi-punti.jpg"],"imageUrl":"https://cdn.example/promo-doppi-punti.jpg","canWinMainPrize":true,"canWinConsolationPrize":false,"mainPrizes":[{"nodeId":"node-3","workflowNodeType":"action","title":"Azione Punti","subtitle":"Wallet: Punti (x2)","actionType":"WALLET","walletId":"1","walletValue":2,"walletOperation":"CHARGE","walletValueType":"MULTIPLIER"}],"consolationPrizes":[],"participationCount":4,"winCount":0},{"id":2,"name":"Buono Benvenuto","description":"Voucher sconto per i nuovi iscritti.","priority":1,"startDate":"2026-01-01T00:00:00.000Z","endDate":"2026-12-31T23:59:59.000Z","imageUrls":["https://cdn.example/promo-buono.jpg"],"imageUrl":"https://cdn.example/promo-buono.jpg","canWinMainPrize":true,"canWinConsolationPrize":false,"mainPrizes":[{"nodeId":"node-5","workflowNodeType":"action","title":"Buono Sconto","subtitle":"€5,00 di sconto","actionType":"COUPON","voucherSetId":12}],"consolationPrizes":[],"participationCount":1,"winCount":0},{"id":3,"name":"Instant Win Estate","description":"Premi a estrazione fino a esaurimento.","priority":2,"startDate":"2026-06-01T00:00:00.000Z","endDate":"2026-08-31T23:59:59.000Z","imageUrls":["https://cdn.example/promo-instant-win.jpg"],"imageUrl":"https://cdn.example/promo-instant-win.jpg","canWinMainPrize":true,"canWinConsolationPrize":false,"mainPrizes":[{"nodeId":"node-iw","workflowNodeType":"instantwin","title":"Instant Win","subtitle":"100 premi in palio","actionType":"INSTANT_WIN","quantity":100,"drawMode":"TIME","uniformByShop":false,"uniformByDuration":true}],"consolationPrizes":[],"participationCount":0,"winCount":0},{"id":4,"name":"Bel Tempo","description":"Regala punti se meteo ok; buono se tier KO.","priority":3,"startDate":"2026-02-19T14:00:00.000Z","endDate":"2026-04-30T08:59:00.000Z","imageUrls":["https://cdn.example/promo-bel-tempo.jpg"],"imageUrl":"https://cdn.example/promo-bel-tempo.jpg","canWinMainPrize":false,"canWinConsolationPrize":true,"mainPrizes":[],"consolationPrizes":[{"nodeId":"node-4","workflowNodeType":"action","title":"Buono Sconto","subtitle":"€2,50 di sconto","actionType":"COUPON","voucherSetId":1}],"participationCount":12,"winCount":2},{"id":5,"name":"Compleanno Gold","description":"Comunicazione + premio catalogo per tier Gold.","priority":4,"startDate":"2026-01-01T00:00:00.000Z","endDate":"2026-12-31T23:59:59.000Z","imageUrls":[],"imageUrl":null,"canWinMainPrize":true,"canWinConsolationPrize":false,"mainPrizes":[{"nodeId":"node-comm","workflowNodeType":"communicate","title":"Auguri","subtitle":"Email + push","actionType":"COMMUNICATE","channels":["EMAIL","PUSH"]},{"nodeId":"node-reward","workflowNodeType":"action","title":"Premio catalogo","subtitle":"Tazza brandizzata","actionType":"REWARD","prizeId":7}],"consolationPrizes":[],"participationCount":0,"winCount":0}]}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/customers/{id}/promosCannotWin":{"get":{"tags":["Customers"],"description":"Solo clienti **ACTIVATE**; altrimenti `promotions: []`. Promo che il cliente **non può più vincere**: chiuse/inattive/scadute oppure ancora attive ma con limite vincite per cliente esaurito. Include `winCount` (vincite del cliente), `participationCount`, `cannotWinReason`, `status`. Stessi campi premio/immagini di promosCanWin. JWT `CUSTOMER` solo il proprio `id`. Con token **CUSTOMER** non compaiono promo con `notVisibleToCustomer`.\n\n**Contesto:** I **Clienti** sono gli iscritti al programma fedeltà: anagrafica, tier, carte, saldi wallet, consensi, promo vinte e storico movimenti.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `id` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Solo cliente ACTIVATE; altrimenti lista vuota. Promo non più vincibili: cannotWinReason, status, winCount, participationCount.\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Customers.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Solo cliente ACTIVATE; altrimenti lista vuota. Promo non più vincibili: cannotWinReason, status, winCount, participationCount.","content":{"application/json":{"schema":{"description":"Solo cliente ACTIVATE; altrimenti lista vuota. Promo non più vincibili: cannotWinReason, status, winCount, participationCount.","type":"object","additionalProperties":true,"example":{"promotions":[{"id":2,"name":"Promo Estate 2025","description":"Promo terminata.","priority":1,"startDate":"2025-06-01T00:00:00.000Z","endDate":"2025-08-31T23:59:59.000Z","imageUrls":[],"imageUrl":null,"status":"FINISHED","cannotWinReason":"EXPIRED","canWinMainPrize":false,"canWinConsolationPrize":false,"mainPrizes":[],"consolationPrizes":[],"participationCount":5,"winCount":1},{"id":3,"name":"Instant Win Natale","description":"Hai già vinto il massimo consentito.","priority":2,"startDate":"2026-11-01T00:00:00.000Z","endDate":"2026-12-31T23:59:59.000Z","imageUrls":["https://cdn.example/promo-natale.jpg"],"imageUrl":"https://cdn.example/promo-natale.jpg","status":"ACTIVE","cannotWinReason":"LIMIT_REACHED","canWinMainPrize":false,"canWinConsolationPrize":false,"mainPrizes":[{"nodeId":"node-iw","workflowNodeType":"instantwin","title":"Instant Win","subtitle":null,"actionType":"INSTANT_WIN","quantity":100,"drawMode":"TIME"}],"consolationPrizes":[],"participationCount":8,"winCount":1}]}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/customers/{id}/tags":{"get":{"tags":["Customers"],"description":"Elenco paginato di tag.\n\n**Contesto:** I **Clienti** sono gli iscritti al programma fedeltà: anagrafica, tier, carte, saldi wallet, consensi, promo vinte e storico movimenti.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `id` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Tag associati al cliente.\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Customers.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Tag associati al cliente.","content":{"application/json":{"schema":{"description":"Tag associati al cliente.","type":"object","additionalProperties":true,"example":{"content":[{"id":6,"name":"VIP","description":"Cliente VIP","tagType":"customers","color":"green","iconPath":null,"assignedAt":"2026-04-02T13:48:01.237Z"}],"totalElements":1}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/customers/{id}/badges":{"get":{"tags":["Customers"],"description":"Elenco paginato di badge.\n\n**Contesto:** I **Clienti** sono gli iscritti al programma fedeltà: anagrafica, tier, carte, saldi wallet, consensi, promo vinte e storico movimenti.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `id` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Badge associati al cliente.\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Customers.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Badge associati al cliente.","content":{"application/json":{"schema":{"description":"Badge associati al cliente.","type":"object","additionalProperties":true,"example":{"content":[{"id":3,"name":"Benvenuto","description":"Badge di benvenuto","categoryId":1,"category":{"id":1,"name":"Generale","description":null,"programId":1,"createdAt":"2026-04-02T13:48:01.237Z","updatedAt":"2026-04-02T13:48:01.237Z"},"obtainedAt":"2026-04-02T13:48:01.237Z"}],"totalElements":1}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/customers/search/by-card":{"get":{"tags":["Customers"],"description":"Elenco paginato di by-card.\n\n**Contesto:** I **Clienti** sono gli iscritti al programma fedeltà: anagrafica, tier, carte, saldi wallet, consensi, promo vinte e storico movimenti.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Querystring:**\n- `card` (string) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Dettaglio cliente (wallet/card filtrati per JWT CUSTOMER se applicabile).\n\n**Errori documentati:** 400, 401, 403, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Customers.","parameters":[{"schema":{"type":"string"},"in":"query","name":"card","required":true,"description":"Numero card o foreignCode"}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Dettaglio cliente (wallet/card filtrati per JWT CUSTOMER se applicabile).","content":{"application/json":{"schema":{"description":"Dettaglio cliente (wallet/card filtrati per JWT CUSTOMER se applicabile).","type":"object","additionalProperties":true,"example":{"id":2,"foreignId":"CRM-0002","firstName":"Maria","lastName":"Bianchi","email":"maria.bianchi@example.com","phone":"+393331112233","notes":"Cliente VIP — preferisce contatto telefonico","cardNumber":"10000001","customerToken":"K7Q2M9","status":"ACTIVATE","programId":1,"avatarUrl":"https://cdn.example.com/1/Avatars/2-a1b2c3d4.jpg","tierId":5,"tierName":"Gold","cards":[{"id":1,"cardNumber":"10000001","foreignCode":"CARD-EXT-001","tierId":5,"tierName":"Gold","isPrimary":true,"status":"ACTIVE","createdAt":"2026-03-13T09:35:16.033Z","updatedAt":"2026-03-13T09:35:16.033Z","wallets":[{"walletId":1,"symbol":"PT","name":"Punti","decimalPlaces":2,"useCardContainers":false,"balance":7826.08,"chargedTotal":1378708.08,"dischargedTotal":1370882,"lastTransactionAt":"2026-04-02T13:48:01.237Z"},{"walletId":2,"symbol":"ST","name":"Status","decimalPlaces":0,"useCardContainers":false,"balance":24954,"chargedTotal":24954,"dischargedTotal":0,"lastTransactionAt":"2026-03-17T16:34:59.878Z"}]},{"id":4,"cardNumber":"10000002","foreignCode":null,"tierId":6,"tierName":"Argento","isPrimary":false,"status":"ACTIVE","createdAt":"2026-03-13T10:38:31.289Z","updatedAt":"2026-03-13T10:38:31.289Z","wallets":[]}],"gender":"F","birthdate":"1985-05-15T00:00:00.000Z","identityCard":"BNCMRA85E55Z600X","identityCardTypeId":null,"taxNumber":"01234567890","registrationShopId":null,"registrationNetId":null,"addressId":3,"address":{"id":3,"street":"Via Roma","number":"10","floor":"","apartment":"","zipCode":"50100","city":"Firenze","state":"FI","country":"IT","latitude":43.7696,"longitude":11.2558},"wallets":[{"id":1,"name":"Punti","symbol":"PT","decimalPlaces":2,"useCardContainers":false,"balance":7826.08,"chargedTotal":1378708.08,"dischargedTotal":1370882,"level":"Fuoriclasse","chargeCount":25256,"dischargeCount":6,"lastTransactionAt":"2026-04-02T13:48:01.237Z"},{"id":2,"name":"Status","symbol":"ST","decimalPlaces":0,"useCardContainers":false,"balance":24954,"chargedTotal":24954,"dischargedTotal":0,"level":null,"chargeCount":12474,"dischargeCount":0,"lastTransactionAt":"2026-03-17T16:34:59.878Z"}],"tags":[{"id":6,"name":"VIP","description":"Cliente VIP","tagType":"customers","color":"green","iconPath":"","assignedAt":"2026-03-31T19:06:17.238Z"}],"vouchers":{"total":0,"active":0},"dynamicFields":[{"dynamicFieldId":1,"dynamicFieldName":"Colore preferito","value":"blue","updatedAt":"2026-04-02T13:45:27.173Z"}],"consents":[{"consentDefinitionId":1,"name":"Privacy","description":"Consenso alla Privacy","required":false,"expiryMonths":24,"granted":false,"grantedAt":null,"expiresAt":null},{"consentDefinitionId":2,"name":"Marketing","description":"Consenso al Marketing","required":false,"expiryMonths":0,"granted":false,"grantedAt":"2026-04-02T14:53:51.457Z","expiresAt":"2026-04-02T14:53:51.457Z"}],"parentCustomerId":10,"familyCanDischargeOverride":false,"familyParent":{"id":10,"firstName":"Giovanni","lastName":"Bianchi","email":"giovanni.bianchi@example.com","phone":"+393334445566","status":"ACTIVATE","cardNumber":"10000000","customerToken":"A1B2C3"},"familyChildren":[{"id":3,"firstName":"Luca","lastName":"Bianchi","email":"luca.bianchi@example.com","phone":null,"status":"ACTIVATE","cardNumber":"10000003","customerToken":"D4E5F6","familyCanDischargeOverride":true}],"preferredShops":[],"revenueTiers":{"previousRevenue":12000,"currentRevenue":9800,"achievedPercent":81.667,"currentFromDate":"2026-01-01","mode":"previous_revenue","previousCalculatedValueId":12,"currentLevelPercentage":80,"currentLevelTierId":5,"levels":[{"percentage":50,"tierId":4,"tierName":"Silver","reached":true},{"percentage":80,"tierId":5,"tierName":"Gold","reached":true},{"percentage":100,"tierId":6,"tierName":"Platinum","reached":false}]},"lastUse":"2026-04-01T13:48:44.852Z","createdAt":"2026-02-20T08:26:04.781Z","updatedAt":"2026-04-02T13:45:27.114Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/customers/purge":{"post":{"tags":["Customers"],"description":"Anonymize customer data (GDPR purge). Sets name to \"*\", clears identity_card/phone/email/username, deletes address if not shared.\n\n**Contesto:** I **Clienti** sono gli iscritti al programma fedeltà: anagrafica, tier, carte, saldi wallet, consensi, promo vinte e storico movimenti.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Body JSON:**\n- `customerId` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Anonimizzazione eseguita.\n\n**Errori documentati:** 400, 401, 403, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Customers.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["customerId"],"properties":{"customerId":{"type":"integer"}}}}},"required":true},"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Anonimizzazione eseguita.","content":{"application/json":{"schema":{"description":"Anonimizzazione eseguita.","type":"object","additionalProperties":true,"example":{"purged":true,"customerId":2}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/customers/purgeOtp":{"post":{"tags":["Customers"],"description":"Anonymize customer data (GDPR purge) without Bearer token. Requires header X-API-Key for the program and a valid OTP (from POST /api/v1/customers/sendOtp) matching the same email or mobile.\n\n**Contesto:** I **Clienti** sono gli iscritti al programma fedeltà: anagrafica, tier, carte, saldi wallet, consensi, promo vinte e storico movimenti.\n\n**Autenticazione:** endpoint pubblico — non richiede `Authorization: Bearer` (può richiedere `X-API-Key` e/o OTP a seconda del caso).\n\n**Body JSON:**\n- `programId` (integer) — obbligatorio\n- `email` (string) — opzionale\n- `mobile` (string) — opzionale\n- `devicetype` (string) — opzionale\n- `otp_code` (string) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Anonimizzazione eseguita.\n\n**Errori documentati:** 400, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Customers.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["programId","otp_code"],"properties":{"programId":{"type":"integer"},"email":{"type":"string","format":"email"},"mobile":{"type":"string"},"devicetype":{"type":"string"},"otp_code":{"type":"string"}}}}},"required":true},"security":[{"apiKey":[]}],"responses":{"200":{"description":"Anonimizzazione eseguita.","content":{"application/json":{"schema":{"description":"Anonimizzazione eseguita.","type":"object","additionalProperties":true,"example":{"purged":true,"customerId":2}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/customers/{id}/setUsernamePassword":{"post":{"tags":["Customers"],"description":"Crea setUsernamePassword.\n\n**Contesto:** I **Clienti** sono gli iscritti al programma fedeltà: anagrafica, tier, carte, saldi wallet, consensi, promo vinte e storico movimenti.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `id` (integer) — obbligatorio\n\n**Body JSON:**\n- `username` (string) — obbligatorio\n- `password` (string) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Credenziali aggiornate.\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Customers.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["username","password"],"properties":{"username":{"type":"string"},"password":{"type":"string","format":"password"}}}}},"required":true},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Credenziali aggiornate.","content":{"application/json":{"schema":{"description":"Credenziali aggiornate.","type":"object","additionalProperties":true,"example":{"id":2,"username":"mario.rossi","accessStatus":"ACTIVATE"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/customers/{id}/activate":{"post":{"tags":["Customers"],"description":"Attiva una activate.\n\n**Contesto:** I **Clienti** sono gli iscritti al programma fedeltà: anagrafica, tier, carte, saldi wallet, consensi, promo vinte e storico movimenti.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `id` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Operazione riuscita.\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Customers.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Operazione riuscita.","content":{"application/json":{"schema":{"description":"Operazione riuscita.","type":"object","additionalProperties":true,"example":{"success":true}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/customers/{id}/block":{"post":{"tags":["Customers"],"description":"Blocca un cliente (impedisce movimenti e accesso).\n\n**Contesto:** I **Clienti** sono gli iscritti al programma fedeltà: anagrafica, tier, carte, saldi wallet, consensi, promo vinte e storico movimenti.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `id` (integer) — obbligatorio\n\n**Body JSON:**\n- `causeId` (integer) — opzionale\n- `reason` (string) — opzionale\n\n**Risposte di successo:**\n- **200**: Operazione riuscita.\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Customers.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"causeId":{"type":"integer"},"reason":{"type":"string"}}}}}},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Operazione riuscita.","content":{"application/json":{"schema":{"description":"Operazione riuscita.","type":"object","additionalProperties":true,"example":{"success":true}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/customers/{id}/unblock":{"post":{"tags":["Customers"],"description":"Sblocca un cliente precedentemente bloccato.\n\n**Contesto:** I **Clienti** sono gli iscritti al programma fedeltà: anagrafica, tier, carte, saldi wallet, consensi, promo vinte e storico movimenti.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `id` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Operazione riuscita.\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Customers.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Operazione riuscita.","content":{"application/json":{"schema":{"description":"Operazione riuscita.","type":"object","additionalProperties":true,"example":{"success":true}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/customers/{id}/onesignal":{"put":{"tags":["Customers"],"description":"Salva l'id di sottoscrizione OneSignal del dispositivo. Lo toglie dagli altri clienti dello stesso programma. Il token CUSTOMER può aggiornare solo il proprio id.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["oneSignalId"],"properties":{"oneSignalId":{"type":"string","nullable":true}}}}},"required":true},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/customers/{id}/pincode":{"put":{"tags":["Customers"],"description":"Aggiorna pincode (campi consentiti).\n\n**Contesto:** I **Clienti** sono gli iscritti al programma fedeltà: anagrafica, tier, carte, saldi wallet, consensi, promo vinte e storico movimenti.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `id` (integer) — obbligatorio\n\n**Body JSON:**\n- `currentPinCode` (string) — opzionale\n- `newPinCode` (string) — opzionale\n\n**Risposte di successo:**\n- **200**: Operazione riuscita.\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Customers.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"currentPinCode":{"type":"string"},"newPinCode":{"type":"string"}}}}}},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Operazione riuscita.","content":{"application/json":{"schema":{"description":"Operazione riuscita.","type":"object","additionalProperties":true,"example":{"success":true}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/customers/{id}/movements":{"get":{"tags":["Customers"],"description":"Get customer movements (with program, customer, shop, terminal, currency descriptions).\n\n**Contesto:** I **Clienti** sono gli iscritti al programma fedeltà: anagrafica, tier, carte, saldi wallet, consensi, promo vinte e storico movimenti.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `id` (integer) — obbligatorio\n\n**Querystring:**\n- `fromDate` (string) — opzionale\n- `toDate` (string) — opzionale\n- `kind` (integer) — opzionale\n- `page` (integer) — opzionale [default: 1]\n- `size` (integer) — opzionale [default: 20]\n\n**Risposte di successo:**\n- **200**: Movimenti del cliente (stessa struttura di GET /api/v1/movements, con promosWon, autoChargeWalletTransactions, walletPaymentsApplied).\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Customers.","parameters":[{"schema":{"type":"string","format":"date"},"in":"query","name":"fromDate","required":false},{"schema":{"type":"string","format":"date"},"in":"query","name":"toDate","required":false},{"schema":{"type":"integer"},"in":"query","name":"kind","required":false},{"schema":{"type":"integer","default":1},"in":"query","name":"page","required":false},{"schema":{"type":"integer","default":20},"in":"query","name":"size","required":false},{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Movimenti del cliente (stessa struttura di GET /api/v1/movements, con promosWon, autoChargeWalletTransactions, walletPaymentsApplied).","content":{"application/json":{"schema":{"description":"Movimenti del cliente (stessa struttura di GET /api/v1/movements, con promosWon, autoChargeWalletTransactions, walletPaymentsApplied).","type":"object","additionalProperties":true,"example":{"content":[{"id":1001,"kind":1,"dateTime":"2026-04-02T13:48:01.237Z","totalMoney":49.99,"originalTotalMoney":null,"programId":1,"customerId":2,"customerCardId":1,"shopId":10,"terminalId":5,"operatorId":null,"status":"APPROVED","ticketId":null,"foreignId":null,"createdAt":"2026-04-02T13:48:01.237Z","operator":null,"program":{"id":1,"name":"Programma fedeltà","description":null},"customer":{"id":2,"firstName":"Maria","lastName":"Bianchi","customerToken":"K7Q2M9"},"shop":{"id":10,"name":"Negozio centro","description":null},"terminal":{"id":5,"name":"Cassa 1","description":null},"currency":{"id":1,"code":"EUR","name":"Euro","symbol":"€"},"promosWon":[],"autoChargeWalletTransactions":[{"walletId":1,"customerCardId":1,"units":50,"walletSymbol":"PT","walletName":"Punti"}],"walletPaymentsApplied":[]}],"totalElements":1,"totalPages":1,"page":1,"size":20}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/customers/{id}/messages":{"get":{"tags":["Customers"],"description":"Notification center: messages already sent or delivered to this customer (EMAIL, SMS, PUSH, WHATSAPP, RCS). Pending and failed rows stay on the operator log.","parameters":[{"schema":{"type":"integer","default":1},"in":"query","name":"page","required":false},{"schema":{"type":"integer","default":20},"in":"query","name":"size","required":false},{"schema":{"type":"string","enum":["EMAIL","SMS","PUSH","WHATSAPP","RCS"]},"in":"query","name":"type","required":false},{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Centro notifiche del cliente: messaggi SENT o DELIVERED (EMAIL, SMS, PUSH, WHATSAPP, RCS).","content":{"application/json":{"schema":{"description":"Centro notifiche del cliente: messaggi SENT o DELIVERED (EMAIL, SMS, PUSH, WHATSAPP, RCS).","type":"object","additionalProperties":true,"example":{"content":[{"id":200,"programId":1,"customerId":2,"messageType":"PUSH","status":"DELIVERED","subject":"Promo","body":"Messaggio push","kindId":11,"objectId":8812,"targetType":"COUPON","targetId":8812,"couponId":8812,"newsId":null,"promotionId":10,"movementId":null,"sentAt":"2026-04-02T13:48:01.237Z","deliveredAt":"2026-04-02T13:48:01.237Z","errorMessage":null,"readed":false,"readedAt":null,"createdAt":"2026-04-02T13:48:01.237Z","updatedAt":"2026-04-02T13:48:01.237Z"}],"totalElements":1,"totalPages":1,"page":1,"size":20}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/customers/{id}/messagesReaded":{"put":{"tags":["Customers"],"description":"Mark one inbox message as read. Works for every channel (not only PUSH). Idempotent: readedAt stays the first timestamp.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["messageId"],"properties":{"messageId":{"type":"integer"}}}}},"required":true},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Messaggio segnato come letto.","content":{"application/json":{"schema":{"description":"Messaggio segnato come letto.","type":"object","additionalProperties":true,"example":{"messageId":200,"readed":true,"readedAt":"2026-04-02T13:48:01.237Z","updatedAt":"2026-04-02T13:48:01.237Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/customers/{id}/messagesUnreadedCount":{"get":{"tags":["Customers"],"description":"Unread count for the customer notification center (SENT/DELIVERED, every channel). Optional type filter.","parameters":[{"schema":{"type":"string","enum":["EMAIL","SMS","PUSH","WHATSAPP","RCS"]},"in":"query","name":"type","required":false},{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Conteggio non letti.","content":{"application/json":{"schema":{"description":"Conteggio non letti.","type":"object","additionalProperties":true,"example":{"customerId":2,"unreadedCount":3}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/customers/{id}/messagesPush":{"get":{"tags":["Customers"],"description":"Elenco paginato di messagesPush.\n\n**Contesto:** I **Clienti** sono gli iscritti al programma fedeltà: anagrafica, tier, carte, saldi wallet, consensi, promo vinte e storico movimenti.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `id` (integer) — obbligatorio\n\n**Querystring:**\n- `page` (integer) — opzionale [default: 1]\n- `size` (integer) — opzionale [default: 20]\n\n**Risposte di successo:**\n- **200**: Lista messaggi push.\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Customers.","parameters":[{"schema":{"type":"integer","default":1},"in":"query","name":"page","required":false},{"schema":{"type":"integer","default":20},"in":"query","name":"size","required":false},{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Lista messaggi push.","content":{"application/json":{"schema":{"description":"Lista messaggi push.","type":"object","additionalProperties":true,"example":{"content":[{"id":200,"programId":1,"customerId":2,"messageType":"PUSH","status":"DELIVERED","subject":"Promo","body":"Messaggio push","kindId":11,"objectId":8812,"targetType":"COUPON","targetId":8812,"couponId":8812,"newsId":null,"promotionId":10,"movementId":null,"sentAt":"2026-04-02T13:48:01.237Z","deliveredAt":"2026-04-02T13:48:01.237Z","errorMessage":null,"readed":false,"readedAt":null,"createdAt":"2026-04-02T13:48:01.237Z","updatedAt":"2026-04-02T13:48:01.237Z"}],"totalElements":1,"totalPages":1,"page":1,"size":20}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/customers/{id}/messagesPushReaded":{"put":{"tags":["Customers"],"description":"Aggiorna messagesPushReaded (campi consentiti).\n\n**Contesto:** I **Clienti** sono gli iscritti al programma fedeltà: anagrafica, tier, carte, saldi wallet, consensi, promo vinte e storico movimenti.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `id` (integer) — obbligatorio\n\n**Body JSON:**\n- `messageId` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Messaggio segnato come letto.\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Customers.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["messageId"],"properties":{"messageId":{"type":"integer"}}}}},"required":true},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Messaggio segnato come letto.","content":{"application/json":{"schema":{"description":"Messaggio segnato come letto.","type":"object","additionalProperties":true,"example":{"messageId":200,"readed":true,"readedAt":"2026-04-02T13:48:01.237Z","updatedAt":"2026-04-02T13:48:01.237Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/customers/{id}/messagesPushUnreadedCount":{"get":{"tags":["Customers"],"description":"Elenco paginato di messagesPushUnreadedCount.\n\n**Contesto:** I **Clienti** sono gli iscritti al programma fedeltà: anagrafica, tier, carte, saldi wallet, consensi, promo vinte e storico movimenti.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `id` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Conteggio non letti.\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Customers.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Conteggio non letti.","content":{"application/json":{"schema":{"description":"Conteggio non letti.","type":"object","additionalProperties":true,"example":{"customerId":2,"unreadedCount":3}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/customers/{id}/merge":{"post":{"tags":["Customers"],"description":"Unione self-service: importa un altro account del cliente autenticato tramite numero carta. Richiede JWT CUSTOMER con `sub` = `:id`. Procedere solo se nome e cognome coincidono. Sposta carta (diventa primary), movimenti e saldi; disattiva la primary precedente; purge del cliente sorgente.\n\n**Contesto:** I **Clienti** sono gli iscritti al programma fedeltà: anagrafica, tier, carte, saldi wallet, consensi, promo vinte e storico movimenti.\n\n**Autenticazione:** richiede JWT Bearer CUSTOMER (`Authorization: Bearer <token>`).\n\n**Parametri path:**\n- `id` (integer) — obbligatorio: ID cliente destinazione (deve coincidere con `sub` del token)\n\n**Body JSON:**\n- `cardNumber` (string) — obbligatorio: numero carta dell'account da unire\n\n**Risposte di successo:**\n- **200**: Cliente destinazione dopo self-merge (dettaglio + metadati merge).\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Customers.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["cardNumber"],"properties":{"cardNumber":{"type":"string","minLength":1}}}}},"required":true},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Cliente destinazione dopo self-merge (dettaglio + metadati merge).","content":{"application/json":{"schema":{"description":"Cliente destinazione dopo self-merge (dettaglio + metadati merge).","type":"object","additionalProperties":true,"example":{"id":2,"foreignId":"CRM-0002","firstName":"Maria","lastName":"Bianchi","email":"maria.bianchi@example.com","phone":"+393331112233","notes":"Cliente VIP — preferisce contatto telefonico","cardNumber":"10000001","customerToken":"K7Q2M9","status":"ACTIVATE","programId":1,"avatarUrl":"https://cdn.example.com/1/Avatars/2-a1b2c3d4.jpg","tierId":5,"tierName":"Gold","cards":[{"id":1,"cardNumber":"10000001","foreignCode":"CARD-EXT-001","tierId":5,"tierName":"Gold","isPrimary":true,"status":"ACTIVE","createdAt":"2026-03-13T09:35:16.033Z","updatedAt":"2026-03-13T09:35:16.033Z","wallets":[{"walletId":1,"symbol":"PT","name":"Punti","decimalPlaces":2,"useCardContainers":false,"balance":7826.08,"chargedTotal":1378708.08,"dischargedTotal":1370882,"lastTransactionAt":"2026-04-02T13:48:01.237Z"},{"walletId":2,"symbol":"ST","name":"Status","decimalPlaces":0,"useCardContainers":false,"balance":24954,"chargedTotal":24954,"dischargedTotal":0,"lastTransactionAt":"2026-03-17T16:34:59.878Z"}]},{"id":4,"cardNumber":"10000002","foreignCode":null,"tierId":6,"tierName":"Argento","isPrimary":false,"status":"ACTIVE","createdAt":"2026-03-13T10:38:31.289Z","updatedAt":"2026-03-13T10:38:31.289Z","wallets":[]}],"gender":"F","birthdate":"1985-05-15T00:00:00.000Z","identityCard":"BNCMRA85E55Z600X","identityCardTypeId":null,"taxNumber":"01234567890","registrationShopId":null,"registrationNetId":null,"addressId":3,"address":{"id":3,"street":"Via Roma","number":"10","floor":"","apartment":"","zipCode":"50100","city":"Firenze","state":"FI","country":"IT","latitude":43.7696,"longitude":11.2558},"wallets":[{"id":1,"name":"Punti","symbol":"PT","decimalPlaces":2,"useCardContainers":false,"balance":7826.08,"chargedTotal":1378708.08,"dischargedTotal":1370882,"level":"Fuoriclasse","chargeCount":25256,"dischargeCount":6,"lastTransactionAt":"2026-04-02T13:48:01.237Z"},{"id":2,"name":"Status","symbol":"ST","decimalPlaces":0,"useCardContainers":false,"balance":24954,"chargedTotal":24954,"dischargedTotal":0,"level":null,"chargeCount":12474,"dischargeCount":0,"lastTransactionAt":"2026-03-17T16:34:59.878Z"}],"tags":[{"id":6,"name":"VIP","description":"Cliente VIP","tagType":"customers","color":"green","iconPath":"","assignedAt":"2026-03-31T19:06:17.238Z"}],"vouchers":{"total":0,"active":0},"dynamicFields":[{"dynamicFieldId":1,"dynamicFieldName":"Colore preferito","value":"blue","updatedAt":"2026-04-02T13:45:27.173Z"}],"consents":[{"consentDefinitionId":1,"name":"Privacy","description":"Consenso alla Privacy","required":false,"expiryMonths":24,"granted":false,"grantedAt":null,"expiresAt":null},{"consentDefinitionId":2,"name":"Marketing","description":"Consenso al Marketing","required":false,"expiryMonths":0,"granted":false,"grantedAt":"2026-04-02T14:53:51.457Z","expiresAt":"2026-04-02T14:53:51.457Z"}],"parentCustomerId":10,"familyCanDischargeOverride":false,"familyParent":{"id":10,"firstName":"Giovanni","lastName":"Bianchi","email":"giovanni.bianchi@example.com","phone":"+393334445566","status":"ACTIVATE","cardNumber":"10000000","customerToken":"A1B2C3"},"familyChildren":[{"id":3,"firstName":"Luca","lastName":"Bianchi","email":"luca.bianchi@example.com","phone":null,"status":"ACTIVATE","cardNumber":"10000003","customerToken":"D4E5F6","familyCanDischargeOverride":true}],"preferredShops":[],"revenueTiers":{"previousRevenue":12000,"currentRevenue":9800,"achievedPercent":81.667,"currentFromDate":"2026-01-01","mode":"previous_revenue","previousCalculatedValueId":12,"currentLevelPercentage":80,"currentLevelTierId":5,"levels":[{"percentage":50,"tierId":4,"tierName":"Silver","reached":true},{"percentage":80,"tierId":5,"tierName":"Gold","reached":true},{"percentage":100,"tierId":6,"tierName":"Platinum","reached":false}]},"lastUse":"2026-04-01T13:48:44.852Z","createdAt":"2026-02-20T08:26:04.781Z","updatedAt":"2026-04-02T13:45:27.114Z","merge":{"sourceCustomerId":15,"importedCardId":1,"importedCardNumber":"10000001","deactivatedCardId":4,"sourcePurged":true}}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/customers/merge":{"post":{"tags":["Customers"],"description":"Unione admin: assorbe `sourceCustomerId` in `targetCustomerId`. Sposta movimenti, wallet_transactions, card (secondarie) e dati loyalty; somma i saldi source sulla primary del target; purge GDPR del source. Non richiede coincidenza di nome/cognome (a differenza del self-merge).\n\n**Contesto:** I **Clienti** sono gli iscritti al programma fedeltà: anagrafica, tier, carte, saldi wallet, consensi, promo vinte e storico movimenti.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Body JSON:**\n- `sourceCustomerId` (integer) — obbligatorio\n- `targetCustomerId` (integer) — obbligatorio\n- `options` (object) — opzionale\n\n**Risposte di successo:**\n- **200**: Cliente destinazione dopo merge admin (dettaglio + metadati: movimenti, wallet e card spostati).\n\n**Errori documentati:** 400, 401, 403, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Customers.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["sourceCustomerId","targetCustomerId"],"properties":{"sourceCustomerId":{"type":"integer"},"targetCustomerId":{"type":"integer"},"options":{"type":"object"}}}}},"required":true},"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Cliente destinazione dopo merge admin (dettaglio + metadati: movimenti, wallet e card spostati).","content":{"application/json":{"schema":{"description":"Cliente destinazione dopo merge admin (dettaglio + metadati: movimenti, wallet e card spostati).","type":"object","additionalProperties":true,"example":{"id":2,"firstName":"Maria","lastName":"Bianchi","email":"maria.bianchi@example.com","phone":"+393331112233","cardNumber":"10000001","customerToken":"K7Q2M9","status":"ACTIVATE","programId":1,"tierId":5,"tierName":"Gold","cards":[{"id":1,"cardNumber":"10000001","foreignCode":"CARD-EXT-001","tierId":5,"tierName":"Gold","isPrimary":true,"status":"ACTIVE","createdAt":"2026-03-13T09:35:16.033Z","updatedAt":"2026-03-13T09:35:16.033Z","wallets":[{"walletId":1,"symbol":"PT","name":"Punti","decimalPlaces":2,"useCardContainers":false,"balance":7826.08,"chargedTotal":1378708.08,"dischargedTotal":1370882,"lastTransactionAt":"2026-04-02T13:48:01.237Z"},{"walletId":2,"symbol":"ST","name":"Status","decimalPlaces":0,"useCardContainers":false,"balance":24954,"chargedTotal":24954,"dischargedTotal":0,"lastTransactionAt":"2026-03-17T16:34:59.878Z"}]},{"id":4,"cardNumber":"10000002","foreignCode":null,"tierId":6,"tierName":"Argento","isPrimary":false,"status":"ACTIVE","createdAt":"2026-03-13T10:38:31.289Z","updatedAt":"2026-03-13T10:38:31.289Z","wallets":[]}],"lastUse":"2026-04-02T13:48:01.237Z","createdAt":"2026-04-02T13:48:01.237Z","updatedAt":"2026-04-02T13:48:01.237Z","merge":{"sourceCustomerId":15,"targetCustomerId":2,"sourcePurged":true,"movedMovements":3,"movedWalletTransactions":2,"transferredCardIds":[8],"targetPrimaryCardId":1}}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/customers/changeRegistrationShop":{"post":{"tags":["Customers"],"description":"Imposta o aggiorna il negozio/rete di registrazione di un cliente esistente. Richiede il permesso dedicato customers:change_registration_shop.\n\n**Contesto:** I **Clienti** sono gli iscritti al programma fedeltà: anagrafica, tier, carte, saldi wallet, consensi, promo vinte e storico movimenti.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Body JSON:**\n- `customerId` (integer) — obbligatorio\n- `registrationShopId` (integer) — opzionale\n- `registrationNetId` (integer) — opzionale\n\n**Risposte di successo:**\n- **200**: Negozio/rete di registrazione aggiornati.\n\n**Errori documentati:** 400, 401, 403, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Customers.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["customerId"],"properties":{"customerId":{"type":"integer"},"registrationShopId":{"type":"integer","nullable":true},"registrationNetId":{"type":"integer","nullable":true}}}}},"required":true},"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Negozio/rete di registrazione aggiornati.","content":{"application/json":{"schema":{"description":"Negozio/rete di registrazione aggiornati.","type":"object","additionalProperties":true,"example":{"customerId":2,"programId":1,"registrationShopId":10,"registrationNetId":null,"previousRegistrationShopId":null,"previousRegistrationNetId":null,"updatedAt":"2026-04-02T13:48:01.237Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/customers/{id}/cards":{"get":{"tags":["Customers"],"description":"Elenco paginato di cards.\n\n**Contesto:** I **Clienti** sono gli iscritti al programma fedeltà: anagrafica, tier, carte, saldi wallet, consensi, promo vinte e storico movimenti.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `id` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Elenco carte con wallet per carta.\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Customers.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Elenco carte con wallet per carta.","content":{"application/json":{"schema":{"description":"Elenco carte con wallet per carta.","type":"object","additionalProperties":true,"example":{"content":[{"id":1,"cardNumber":"10000001","foreignCode":"CARD-EXT-001","tierId":5,"tierName":"Gold","isPrimary":true,"status":"ACTIVE","createdAt":"2026-03-13T09:35:16.033Z","updatedAt":"2026-03-13T09:35:16.033Z","wallets":[{"walletId":1,"symbol":"PT","name":"Punti","decimalPlaces":2,"useCardContainers":false,"balance":7826.08,"chargedTotal":1378708.08,"dischargedTotal":1370882,"lastTransactionAt":"2026-04-02T13:48:01.237Z"},{"walletId":2,"symbol":"ST","name":"Status","decimalPlaces":0,"useCardContainers":false,"balance":24954,"chargedTotal":24954,"dischargedTotal":0,"lastTransactionAt":"2026-03-17T16:34:59.878Z"}]},{"id":4,"cardNumber":"10000002","foreignCode":null,"tierId":6,"tierName":"Argento","isPrimary":false,"status":"ACTIVE","createdAt":"2026-03-13T10:38:31.289Z","updatedAt":"2026-03-13T10:38:31.289Z","wallets":[]}],"totalElements":2}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"post":{"tags":["Customers"],"description":"Crea cards.\n\n**Contesto:** I **Clienti** sono gli iscritti al programma fedeltà: anagrafica, tier, carte, saldi wallet, consensi, promo vinte e storico movimenti.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `id` (integer) — obbligatorio\n\n**Body JSON:**\n- `cardNumber` (string) — obbligatorio\n- `tierId` (integer) — opzionale\n- `isPrimary` (boolean) — opzionale [default: false]\n\n**Risposte di successo:**\n- **201**: Carta creata (singolo oggetto carta).\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Customers.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["cardNumber"],"properties":{"cardNumber":{"type":"string"},"foreignCode":{"type":"string","description":"Codice esterno opzionale (univoco per programma)","nullable":true},"tierId":{"type":"integer","nullable":true},"isPrimary":{"type":"boolean","default":false}}}}},"required":true},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"201":{"description":"Carta creata (singolo oggetto carta).","content":{"application/json":{"schema":{"description":"Carta creata (singolo oggetto carta).","type":"object","additionalProperties":true,"example":{"id":11,"cardNumber":"10000099","foreignCode":"CARD-EXT-099","tierId":5,"tierName":"Gold","isPrimary":false,"status":"ACTIVE","createdAt":"2026-04-02T13:48:01.237Z","updatedAt":"2026-04-02T13:48:01.237Z","wallets":[]}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/customers/{id}/cards/{cardId}/wallet-pass":{"get":{"tags":["Customers"],"description":"Generate a mobile wallet pass for a loyalty card. platform=ios returns Apple .pkpass; platform=android returns Google Wallet saveUrl JSON.","parameters":[{"schema":{"type":"string","enum":["ios","android"]},"in":"query","name":"platform","required":true},{"schema":{"type":"integer"},"in":"path","name":"id","required":true},{"schema":{"type":"integer"},"in":"path","name":"cardId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/customers/{id}/cards/{cardId}/apple-pass":{"get":{"tags":["Customers"],"description":"Alias of wallet-pass?platform=ios — signed Apple Wallet (.pkpass) for a loyalty card.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"id","required":true},{"schema":{"type":"integer"},"in":"path","name":"cardId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/customers/{id}/vouchers/{voucherId}/wallet-pass":{"get":{"tags":["Customers"],"description":"Generate a mobile wallet pass for a voucher/coupon. platform=ios returns Apple .pkpass; platform=android returns Google Wallet saveUrl JSON.","parameters":[{"schema":{"type":"string","enum":["ios","android"]},"in":"query","name":"platform","required":true},{"schema":{"type":"integer"},"in":"path","name":"id","required":true},{"schema":{"type":"integer"},"in":"path","name":"voucherId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/customers/{id}/changeTier":{"put":{"tags":["Customers"],"description":"Aggiorna changeTier (campi consentiti).\n\n**Contesto:** I **Clienti** sono gli iscritti al programma fedeltà: anagrafica, tier, carte, saldi wallet, consensi, promo vinte e storico movimenti.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `id` (integer) — obbligatorio\n\n**Body JSON:**\n- `tierId` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Tier aggiornato sulla carta primaria.\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Customers.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["tierId"],"properties":{"tierId":{"type":"integer"}}}}},"required":true},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Tier aggiornato sulla carta primaria.","content":{"application/json":{"schema":{"description":"Tier aggiornato sulla carta primaria.","type":"object","additionalProperties":true,"example":{"customerId":2,"tierId":6,"tierName":"Argento","cardId":1,"updatedAt":"2026-04-02T13:48:01.237Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/customers/{id}/cards/replace":{"post":{"tags":["Customers"],"description":"Sostituisce la carta del cliente: elimina la vecchia (stato DELETED), crea la nuova con validazione whitelist (come attivazione), trasferisce i saldi wallet sulla nuova carta. Se la nuova card ha un tier di whitelist con level minore di quello attuale, la card viene attivata ma si mantiene il tier più alto.\n\n**Contesto:** I **Clienti** sono gli iscritti al programma fedeltà: anagrafica, tier, carte, saldi wallet, consensi, promo vinte e storico movimenti.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `id` (integer) — obbligatorio\n\n**Body JSON:**\n- `oldCardNumber` (string) — obbligatorio\n- `newCardNumber` (string) — obbligatorio\n- `notes` (string) — opzionale\n\n**Risposte di successo:**\n- **200**: Default Response\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Customers.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["oldCardNumber","newCardNumber"],"properties":{"oldCardNumber":{"type":"string"},"newCardNumber":{"type":"string"},"notes":{"type":"string"}}}}},"required":true},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"customerId":{"type":"integer"},"programId":{"type":"integer"},"oldCustomerCardId":{"type":"integer"},"newCustomerCardId":{"type":"integer"},"oldCardNumber":{"type":"string"},"newCardNumber":{"type":"string"},"tierId":{"type":"integer","nullable":true}},"example":{"customerId":0,"programId":0,"oldCustomerCardId":0,"newCustomerCardId":0,"oldCardNumber":"","newCardNumber":"","tierId":0}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/customers/{id}/cards/nfc-tag":{"post":{"tags":["Customers"],"description":"Associa un UID NFC (MIFARE Ultralight) alla card del cliente. Richiede businessRules.nfcTags.enabled.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["cardNumber","nfcUid"],"properties":{"cardNumber":{"type":"string"},"nfcUid":{"type":"string","description":"UID hex (con o senza prefisso NFC:)"}}}}},"required":true},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"customerId":{"type":"integer"},"programId":{"type":"integer"},"cardNumber":{"type":"string"},"nfcUid":{"type":"string"}},"example":{"customerId":0,"programId":0,"cardNumber":"","nfcUid":""}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/customers/{id}/cards/nfc-tag/unlink":{"post":{"tags":["Customers"],"description":"Rimuove l'UID NFC associato alla card. Se la card è NFC-native (card_number = UID, tipico attivazione POS) viene soft-deleted e sparisce dal cliente. Richiede businessRules.nfcTags.enabled.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["cardNumber"],"properties":{"cardNumber":{"type":"string"}}}}},"required":true},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/customers/{id}/cards/{cardId}":{"put":{"tags":["Customers"],"description":"Update card tier/primary flag/status. POS: consentito con token terminale; il cliente deve appartenere al programId del token.\n\n**Contesto:** I **Clienti** sono gli iscritti al programma fedeltà: anagrafica, tier, carte, saldi wallet, consensi, promo vinte e storico movimenti.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `id` (integer) — obbligatorio\n- `cardId` (integer) — obbligatorio\n\n**Body JSON:**\n- `tierId` (integer) — opzionale\n- `isPrimary` (boolean) — opzionale\n- `status` (string) — opzionale (valori: ACTIVE, BLOCKED)\n\n**Risposte di successo:**\n- **200**: Carta aggiornata (oggetto carta completo).\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Customers.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"tierId":{"type":"integer","nullable":true},"isPrimary":{"type":"boolean"},"status":{"type":"string","enum":["ACTIVE","BLOCKED"]},"foreignCode":{"type":"string","description":"Codice esterno opzionale (univoco per programma)","nullable":true}}}}}},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"id","required":true},{"schema":{"type":"integer"},"in":"path","name":"cardId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Carta aggiornata (oggetto carta completo).","content":{"application/json":{"schema":{"description":"Carta aggiornata (oggetto carta completo).","type":"object","additionalProperties":true,"example":{"id":1,"cardNumber":"10000001","foreignCode":"CARD-EXT-001","tierId":5,"tierName":"Gold","isPrimary":true,"status":"ACTIVE","createdAt":"2026-03-13T09:35:16.033Z","updatedAt":"2026-03-13T09:35:16.033Z","wallets":[{"walletId":1,"symbol":"PT","name":"Punti","decimalPlaces":2,"useCardContainers":false,"balance":7826.08,"chargedTotal":1378708.08,"dischargedTotal":1370882,"lastTransactionAt":"2026-04-02T13:48:01.237Z"},{"walletId":2,"symbol":"ST","name":"Status","decimalPlaces":0,"useCardContainers":false,"balance":24954,"chargedTotal":24954,"dischargedTotal":0,"lastTransactionAt":"2026-03-17T16:34:59.878Z"}]}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"delete":{"tags":["Customers"],"description":"Elimina cards (soft delete o `TERMINATED` se vincoli FK).\n\n**Contesto:** I **Clienti** sono gli iscritti al programma fedeltà: anagrafica, tier, carte, saldi wallet, consensi, promo vinte e storico movimenti.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `id` (integer) — obbligatorio\n- `cardId` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **204**: Nessun corpo (operazione riuscita).\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Customers.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"id","required":true},{"schema":{"type":"integer"},"in":"path","name":"cardId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"204":{"description":"Nessun corpo (operazione riuscita)."},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/customers/{id}/shopsUnique":{"get":{"tags":["Customers"],"description":"Elenco paginato di shopsUnique.\n\n**Contesto:** I **Clienti** sono gli iscritti al programma fedeltà: anagrafica, tier, carte, saldi wallet, consensi, promo vinte e storico movimenti.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `id` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Negozi unici in cui il cliente ha effettuato almeno un acquisto (vendita).\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Customers.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Negozi unici in cui il cliente ha effettuato almeno un acquisto (vendita).","content":{"application/json":{"schema":{"description":"Negozi unici in cui il cliente ha effettuato almeno un acquisto (vendita).","type":"array","items":{"type":"object","additionalProperties":false,"required":["id","name"],"properties":{"id":{"type":"integer","example":10},"name":{"type":"string","example":"Negozio centro"}}},"example":[{"id":10,"name":"Negozio centro"},{"id":22,"name":"Outlet nord"}]}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/customers/{id}/preferredShops":{"get":{"tags":["Customers"],"description":"Elenco paginato di preferredShops.\n\n**Contesto:** I **Clienti** sono gli iscritti al programma fedeltà: anagrafica, tier, carte, saldi wallet, consensi, promo vinte e storico movimenti.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `id` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Negozi preferiti.\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Customers.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Negozi preferiti.","content":{"application/json":{"schema":{"description":"Negozi preferiti.","type":"object","additionalProperties":true,"example":{"customerId":2,"preferredShops":[{"shopId":10,"name":"Negozio centro","description":null,"city":"Firenze","state":"FI","preferredSince":"2026-04-02T13:48:01.237Z"}]}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/customers/{id}/preferredShops/{shopId}":{"put":{"tags":["Customers"],"description":"Aggiorna preferredShops (campi consentiti).\n\n**Contesto:** I **Clienti** sono gli iscritti al programma fedeltà: anagrafica, tier, carte, saldi wallet, consensi, promo vinte e storico movimenti.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `id` (integer) — obbligatorio\n- `shopId` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Negozio aggiunto ai preferiti.\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Customers.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"id","required":true},{"schema":{"type":"integer"},"in":"path","name":"shopId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Negozio aggiunto ai preferiti.","content":{"application/json":{"schema":{"description":"Negozio aggiunto ai preferiti.","type":"object","additionalProperties":true,"example":{"customerId":2,"shopId":10,"preferred":true}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"delete":{"tags":["Customers"],"description":"Elimina preferredShops (soft delete o `TERMINATED` se vincoli FK).\n\n**Contesto:** I **Clienti** sono gli iscritti al programma fedeltà: anagrafica, tier, carte, saldi wallet, consensi, promo vinte e storico movimenti.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `id` (integer) — obbligatorio\n- `shopId` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **204**: Nessun corpo (operazione riuscita).\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Customers.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"id","required":true},{"schema":{"type":"integer"},"in":"path","name":"shopId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"204":{"description":"Nessun corpo (operazione riuscita)."},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/customers/{id}/interestedCategories":{"get":{"tags":["Customers"],"description":"Elenco paginato di interestedCategories.\n\n**Contesto:** I **Clienti** sono gli iscritti al programma fedeltà: anagrafica, tier, carte, saldi wallet, consensi, promo vinte e storico movimenti.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `id` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Categorie negozio di interesse del cliente con nomi localizzati.\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Customers.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Categorie negozio di interesse del cliente con nomi localizzati.","content":{"application/json":{"schema":{"description":"Categorie negozio di interesse del cliente con nomi localizzati.","type":"object","additionalProperties":true,"example":{"customerId":42,"interestedCategories":[{"categoryId":5,"parentId":1,"nameIt":"Ristorazione","nameEn":"Dining","nameEs":null,"nameFr":null,"nameDe":null,"namePt":null,"selectedAt":"2026-05-15T10:00:00.000Z"}]}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"post":{"tags":["Customers"],"description":"Crea interestedCategories.\n\n**Contesto:** I **Clienti** sono gli iscritti al programma fedeltà: anagrafica, tier, carte, saldi wallet, consensi, promo vinte e storico movimenti.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `id` (integer) — obbligatorio\n\n**Body JSON:**\n- `categoryIds` (array) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Sostituisce l'elenco categorie di interesse (categoryIds).\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Customers.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["categoryIds"],"properties":{"categoryIds":{"type":"array","items":{"type":"integer"}}}}}},"required":true},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Sostituisce l'elenco categorie di interesse (categoryIds).","content":{"application/json":{"schema":{"description":"Sostituisce l'elenco categorie di interesse (categoryIds).","type":"object","additionalProperties":true,"example":{"customerId":42,"interestedCategories":[]}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/customers/{id}/family/parent":{"put":{"tags":["Customers"],"description":"Imposta o rimuove il cliente padre (family cards / gruppo wallet). Richiede familyCards.enabled sul programma.\n\n**Contesto:** I **Clienti** sono gli iscritti al programma fedeltà: anagrafica, tier, carte, saldi wallet, consensi, promo vinte e storico movimenti.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `id` (integer) — obbligatorio\n\n**Body JSON:**\n- `parentCustomerId` (integer) — opzionale\n\n**Risposte di successo:**\n- **200**: Imposta o rimuove il cliente padre (family cards).\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Customers.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"parentCustomerId":{"type":"integer","nullable":true}}}}}},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Imposta o rimuove il cliente padre (family cards).","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"description":"Imposta o rimuove il cliente padre (family cards).","example":{"customerId":42,"parentCustomerId":10}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/customers/{id}/family/discharge-override":{"patch":{"tags":["Customers"],"description":"Consente a un cliente (es. figlio) di scaricare dal wallet di gruppo anche se non è l'apice.\n\n**Contesto:** I **Clienti** sono gli iscritti al programma fedeltà: anagrafica, tier, carte, saldi wallet, consensi, promo vinte e storico movimenti.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `id` (integer) — obbligatorio\n\n**Body JSON:**\n- `familyCanDischargeOverride` (boolean) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Override scarico wallet di gruppo per cliente figlio.\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Customers.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["familyCanDischargeOverride"],"properties":{"familyCanDischargeOverride":{"type":"boolean"}}}}},"required":true},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Override scarico wallet di gruppo per cliente figlio.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"description":"Override scarico wallet di gruppo per cliente figlio.","example":{"customerId":42,"familyCanDischargeOverride":true}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/pos/resolve":{"get":{"tags":["POS"],"description":"Identifica un codice letto al POS con waterfall: carta cliente → buono sconto → coupon → gift card → telefono cliente.\n\n**Contesto:** Endpoint dedicato al **POS** (terminale): sostituisce le chiamate multiple del client (by-card, voucher, coupon, …) con un'unica ricerca lato backend.\n\n**Autenticazione:** richiede JWT Bearer con `operatorType=POS` (`Authorization: Bearer <token>`). Può essere richiesto anche `X-API-Key`.\n\n**Querystring:**\n- `code` (string) — obbligatorio: numero card, `foreignCode` della card, buono, coupon, gift card o numero di telefono (con o senza prefisso internazionale)\n\n**Risposte di successo:**\n- **200**: Oggetto con `matchType` (`CUSTOMER_CARD`, `DISCOUNT_VOUCHER`, `COUPON`, `GIFT_CARD`, `INACTIVE_GIFT_CARD`, `CUSTOMER_BY_PHONE`) e payload correlato (`customer`, `voucher`, `coupon`, `giftCardPurchased`, `inactiveCard`, `customersByPhone`). Per le carte cliente, `matchedCardNumber` restituisce il numero card effettivo anche se il codice letto era un `foreignCode`.\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** POS.","parameters":[{"schema":{"type":"string"},"in":"query","name":"code","required":true,"description":"Numero card, foreignCode della card, buono, coupon, gift card o numero di telefono (con o senza prefisso internazionale)"}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"matchType":{"type":"string","enum":["CUSTOMER_CARD","DISCOUNT_VOUCHER","COUPON","GIFT_CARD","INACTIVE_GIFT_CARD","CUSTOMER_BY_PHONE","UNASSIGNED_NFC_TAG","OPERATOR_NFC_TAG"]},"inputCode":{"type":"string"},"matchedCardNumber":{"type":"string","nullable":true},"matchedForeignCode":{"type":"string","nullable":true},"nfcUid":{"type":"string","nullable":true},"isGiftCard":{"type":"boolean","nullable":true},"isBlocked":{"type":"boolean","nullable":true},"preselectedVoucher":{"type":"boolean","nullable":true},"ambiguous":{"type":"boolean","nullable":true},"normalizedPhone":{"type":"string","nullable":true},"customer":{"type":"object","nullable":true,"additionalProperties":true},"voucher":{"type":"object","nullable":true,"additionalProperties":true},"coupon":{"type":"object","nullable":true,"additionalProperties":true},"giftCardPurchased":{"type":"object","nullable":true,"description":"Gift card digitale collegata (saldo, codice, valuta). Presente con matchType GIFT_CARD o carta fisica gift card.","additionalProperties":true},"inactiveCard":{"type":"object","nullable":true,"additionalProperties":true},"customersByPhone":{"type":"array","nullable":true,"items":{"type":"object","additionalProperties":true}},"operator":{"type":"object","nullable":true,"description":"Presente con matchType OPERATOR_NFC_TAG.","additionalProperties":true}},"example":{"matchType":"CUSTOMER_CARD","inputCode":"","matchedCardNumber":"","matchedForeignCode":"","nfcUid":"","isGiftCard":false,"isBlocked":false,"preselectedVoucher":false,"ambiguous":false,"normalizedPhone":"","customer":null,"voucher":null,"coupon":null,"giftCardPurchased":null,"inactiveCard":null,"customersByPhone":[],"operator":null}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/pos/closure-summary":{"get":{"tags":["POS"],"description":"Riepilogo movimentazione del terminale POS dall'ultima chiusura: carichi, scarichi, attivazioni, clienti unici, prodotti FE (se modalità FrontEnd) e totali per operatore. Solo token POS.","parameters":[{"schema":{"type":"string"},"in":"query","name":"from","required":false,"description":"ISO datetime inizio periodo (default: inizio giornata corrente)"},{"schema":{"type":"string"},"in":"query","name":"to","required":false,"description":"ISO datetime fine periodo (default: ora corrente)"}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/pos/closures":{"post":{"tags":["POS"],"description":"Registra una chiusura POS: calcola il riepilogo dall'ultima chiusura (o `from`), lo persiste e restituisce lo snapshot. Solo token POS.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"from":{"type":"string","description":"ISO inizio periodo (default: ultima chiusura terminale)"},"to":{"type":"string","description":"ISO fine periodo (default: ora)"}}}}}},"security":[{"bearerAuth":[]}],"responses":{"201":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/pos/closures/last":{"get":{"tags":["POS"],"description":"Ultima chiusura POS registrata per il terminale del token. Solo token POS.","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/pos/nfc-tags/activate":{"post":{"tags":["POS"],"description":"Attiva un tag NFC non ancora associato. Il telefono è obbligatorio se businessRules.nfcTags.askPhoneOnActivation è attivo (default): se il numero esiste già e customers.phoneUnique è attivo risponde 409 NFC_PHONE_ALREADY_EXISTS; con linkToCustomerId aggiunge il tag al cliente. Richiede businessRules.nfcTags.enabled e nfcTags.activateFromPos. Solo token POS.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["nfcUid"],"properties":{"nfcUid":{"type":"string","description":"UID hex del tag (con o senza prefisso NFC:)"},"phone":{"type":"string","description":"Numero di telefono del cliente (obbligatorio se askPhoneOnActivation attivo)"},"linkToCustomerId":{"type":"integer","description":"Se valorizzato, collega il tag a questo cliente (stesso telefono)"},"walletId":{"type":"integer","description":"Wallet per carica iniziale opzionale (attiva + charge + WhatsApp tag_attivato)"},"initialUnits":{"type":"number","description":"Unità di carica iniziale (se > 0 richiede walletId)"}}}}},"required":true},"security":[{"bearerAuth":[]}],"responses":{"201":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"matchType":{"type":"string"},"nfcUid":{"type":"string"},"matchedCardNumber":{"type":"string"},"customerId":{"type":"integer"},"customerCardId":{"type":"integer"},"customer":{"type":"object","additionalProperties":true},"linkedToExisting":{"type":"boolean"},"isGiftCard":{"type":"boolean"},"isBlocked":{"type":"boolean"},"initialCharge":{"type":"object","additionalProperties":true,"nullable":true}},"example":{"matchType":"","nfcUid":"","matchedCardNumber":"","customerId":0,"customerCardId":0,"linkedToExisting":false,"isGiftCard":false,"isBlocked":false,"initialCharge":null}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/pos/custSendOtp":{"post":{"tags":["POS"],"description":"Invia OTP a email e/o mobile di un cliente del programma del token POS. Equivalente a POST /api/v1/customers/sendOtp ma autentica con Bearer POS (non richiede X-API-Key) e verifica che il cliente esista nel programma.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":"string","format":"email"},"mobile":{"type":"string"},"devicetype":{"type":"string"}}}}}},"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"example":{"ok":false}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/pos/sendRegistrationOtp":{"post":{"tags":["POS"],"description":"Invia un OTP SMS al cellulare indicato per la creazione cliente da POS. Non richiede che il cliente esista. Il codice si verifica in POST /api/v1/customers (campo phoneOtp).","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["mobile"],"properties":{"mobile":{"type":"string"}}}}},"required":true},"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"example":{"ok":false}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/pos/terminal-bootstrap":{"get":{"tags":["POS"],"description":"Lookup pubblico del terminale POS per serialNumber o terminalCode. Restituisce flag business rules utili prima del login (es. login operatori via NFC).","parameters":[{"schema":{"type":"string"},"in":"query","name":"serialNumber","required":false,"description":"Serial number hardware del POS"},{"schema":{"type":"string","minLength":1,"maxLength":64},"in":"query","name":"terminalCode","required":false,"description":"Codice terminale oppure numero seriale"}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"found":{"type":"boolean"},"programId":{"type":"integer","nullable":true},"shopId":{"type":"integer","nullable":true},"terminalId":{"type":"integer","nullable":true},"nfcTagsEnabled":{"type":"boolean"},"operatorLoginTagsEnabled":{"type":"boolean"}},"example":{"found":false,"programId":0,"shopId":0,"terminalId":0,"nfcTagsEnabled":false,"operatorLoginTagsEnabled":false}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/pos/sendOtp":{"post":{"tags":["POS"],"description":"Invia OTP email per reset password operatore POS. Richiede email (username) e terminalCode. Risponde sempre ok per evitare enumeration.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["email","terminalCode"],"properties":{"email":{"type":"string","description":"Email o username operatore"},"terminalCode":{"type":"string","minLength":1,"description":"Codice terminale (8 caratteri)"}}}}},"required":true},"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"example":{"ok":false}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/pos/resetPassword":{"post":{"tags":["POS"],"description":"Reset password operatore POS con OTP inviato via email. Richiede email, otp, password e terminalCode.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["email","terminalCode","otp","password"],"properties":{"email":{"type":"string","description":"Email o username operatore"},"terminalCode":{"type":"string","minLength":1,"description":"Codice terminale (8 caratteri)"},"otp":{"type":"string","description":"Codice OTP a 6 cifre"},"password":{"type":"string","format":"password","description":"Nuova password"}}}}},"required":true},"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"example":{"ok":false}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/passkit/v1/devices/{deviceLibraryIdentifier}/registrations/{passTypeIdentifier}/{serialNumber}":{"post":{"parameters":[{"schema":{"type":"string"},"in":"path","name":"deviceLibraryIdentifier","required":true},{"schema":{"type":"string"},"in":"path","name":"passTypeIdentifier","required":true},{"schema":{"type":"string"},"in":"path","name":"serialNumber","required":true}],"responses":{"200":{"description":"Default Response"}}},"delete":{"parameters":[{"schema":{"type":"string"},"in":"path","name":"deviceLibraryIdentifier","required":true},{"schema":{"type":"string"},"in":"path","name":"passTypeIdentifier","required":true},{"schema":{"type":"string"},"in":"path","name":"serialNumber","required":true}],"responses":{"200":{"description":"Default Response"}}}},"/passkit/v1/devices/{deviceLibraryIdentifier}/registrations/{passTypeIdentifier}":{"get":{"parameters":[{"schema":{"type":"string"},"in":"path","name":"deviceLibraryIdentifier","required":true},{"schema":{"type":"string"},"in":"path","name":"passTypeIdentifier","required":true}],"responses":{"200":{"description":"Default Response"}}}},"/passkit/v1/passes/{passTypeIdentifier}/{serialNumber}":{"get":{"parameters":[{"schema":{"type":"string"},"in":"path","name":"passTypeIdentifier","required":true},{"schema":{"type":"string"},"in":"path","name":"serialNumber","required":true}],"responses":{"200":{"description":"Default Response"}}}},"/passkit/v1/log":{"post":{"responses":{"200":{"description":"Default Response"}}}},"/api/v1/public/voucher-wallet-pass":{"get":{"summary":"Download voucher Wallet pass via WhatsApp deep-link token (query)","tags":["Public","Wallet"],"parameters":[{"schema":{"type":"string","minLength":8},"in":"query","name":"token","required":true},{"schema":{"type":"string","enum":["ios","android"]},"in":"query","name":"platform","required":true}],"responses":{"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/public/voucher-wallet-pass/{*}":{"get":{"summary":"Download voucher Wallet pass via WhatsApp deep-link token (path)","tags":["Public","Wallet"],"parameters":[{"schema":{"type":"string","enum":["ios","android"]},"in":"query","name":"platform","required":true},{"schema":{"type":"string"},"in":"path","name":"*","required":true}],"responses":{"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/movements":{"get":{"tags":["Movements"],"description":"Elenco paginato di movimento.\n\n**Contesto:** I **Movimenti** sono transazioni del loyalty: vendite, rimborsi, open movement, riscatti. Registrano importi, wallet coinvolti, promo applicate e terminale/negozio.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Querystring:**\n- `programId` (integer) — opzionale: Obbligatorio per backend; per CUSTOMER/POS deriva dal token se omesso\n- `customerId` (integer) — opzionale: Filtra per cliente (CUSTOMER: solo sé stesso)\n- `customerCardId` (integer) — opzionale: Carta cliente su cui applicare wallet payments e auto-charge\n- `shopId` (integer) — opzionale\n- `terminalId` (integer) — opzionale\n- `page` (integer) — opzionale [default: 1]\n- `size` (integer) — opzionale [default: 20]\n- `dateFrom` (string) — opzionale: Filtra movimenti da questa data (inclusa)\n- `dateTo` (string) — opzionale: Filtra movimenti fino a questa data (inclusa)\n- `kind` (integer) — opzionale: Filtra per tipo movimento\n- `amountFrom` (number) — opzionale: Importo minimo (total_money)\n- `amountTo` (number) — opzionale: Importo massimo (total_money)\n- `ticketId` (string) — opzionale: Filtra per ticket (match esatto)\n- `foreignId` (string) — opzionale: Filtra per foreign_id (match esatto)\n- `promoWonId` (integer) — opzionale: Filtra movimenti che hanno vinto questa promozione\n- `orderBy` (string) — opzionale: Ordina per: id, kind (tipo), dateTime (data/ora), amount (importo) (valori: id, kind, dateTime, amount)\n- `orderDir` (string) — opzionale: Direzione ordinamento (valori: ASC, DESC)\n\n**Risposte di successo:**\n- **200**: Lista movimenti\n\n**Errori documentati:** 400, 401, 403, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Movements.","parameters":[{"schema":{"type":"integer"},"in":"query","name":"programId","required":false,"description":"Obbligatorio per backend; per CUSTOMER/POS deriva dal token se omesso"},{"schema":{"type":"integer"},"in":"query","name":"customerId","required":false,"description":"Filtra per cliente (CUSTOMER: solo sé stesso)"},{"schema":{"type":"integer"},"in":"query","name":"customerCardId","required":false,"description":"Carta cliente su cui applicare wallet payments e auto-charge"},{"schema":{"type":"integer"},"in":"query","name":"shopId","required":false},{"schema":{"type":"integer"},"in":"query","name":"terminalId","required":false},{"schema":{"type":"integer","default":1},"in":"query","name":"page","required":false},{"schema":{"type":"integer","default":20},"in":"query","name":"size","required":false},{"schema":{"type":"boolean","default":true},"in":"query","name":"includeTotals","required":false,"description":"Se false, salta le card (importi, clienti, negozi, wallet, promo) e calcola solo il conteggio del paginatore."},{"schema":{"type":"boolean","default":false},"in":"query","name":"totalsOnly","required":false,"description":"Se true, restituisce solo i totali, senza la pagina di movimenti."},{"schema":{"type":"string","format":"date"},"in":"query","name":"dateFrom","required":false,"description":"Filtra movimenti da questa data (inclusa)"},{"schema":{"type":"string","format":"date"},"in":"query","name":"dateTo","required":false,"description":"Filtra movimenti fino a questa data (inclusa)"},{"schema":{"type":"integer"},"in":"query","name":"kind","required":false,"description":"Filtra per tipo movimento"},{"schema":{"type":"string"},"in":"query","name":"kinds","required":false,"description":"Filtra per più tipi (CSV, es. \"1,100,101\"). Ignorato se `kind` è valorizzato."},{"schema":{"type":"number"},"in":"query","name":"amountFrom","required":false,"description":"Importo minimo: confronta GREATEST(total_money, somma |wallet_transactions|) così filtra anche carichi/scarichi wallet (kind 100/101) con total_money=0"},{"schema":{"type":"number"},"in":"query","name":"amountTo","required":false,"description":"Importo massimo: confronta GREATEST(total_money, somma |wallet_transactions|)"},{"schema":{"type":"string"},"in":"query","name":"ticketId","required":false,"description":"Filtra per ticket (match esatto)"},{"schema":{"type":"string"},"in":"query","name":"foreignId","required":false,"description":"Filtra per foreign_id (match esatto)"},{"schema":{"type":"integer"},"in":"query","name":"promoWonId","required":false,"description":"Filtra movimenti che hanno vinto questa promozione"},{"schema":{"type":"integer"},"in":"query","name":"tierId","required":false,"description":"Filtra movimenti della carta usata, o della carta primaria se il movimento non ha carta"},{"schema":{"type":"integer"},"in":"query","name":"productId","required":false,"description":"Filtra movimenti che includono questo prodotto"},{"schema":{"type":"string","enum":["id","kind","dateTime","amount"]},"in":"query","name":"orderBy","required":false,"description":"Ordina per: id, kind (tipo), dateTime (data/ora), amount (importo)"},{"schema":{"type":"string","enum":["ASC","DESC"]},"in":"query","name":"orderDir","required":false,"description":"Direzione ordinamento"}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Lista movimenti","content":{"application/json":{"schema":{"description":"Lista movimenti","type":"object","additionalProperties":true,"example":{"content":[{"id":1001,"kind":1,"dateTime":"2026-01-15T10:00:00.000Z","totalMoney":49.99,"programId":1,"customerId":2,"customerCardId":1,"shopId":1,"terminalId":1,"status":"APPROVED","currency":{"id":1,"code":"EUR","name":"Euro","symbol":"€"},"promosWon":[],"autoChargeWalletTransactions":[],"walletPaymentsApplied":[],"createdAt":"2026-01-15T10:00:00.000Z"}],"totalElements":1,"totalPages":1,"page":1,"size":20}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/movements/last":{"get":{"tags":["Movements"],"description":"Elenco paginato di last.\n\n**Contesto:** I **Movimenti** sono transazioni del loyalty: vendite, rimborsi, open movement, riscatti. Registrano importi, wallet coinvolti, promo applicate e terminale/negozio.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Querystring:**\n- `programId` (integer) — opzionale: Obbligatorio per backend; per CUSTOMER/POS deriva dal token se omesso\n- `customerId` (integer) — opzionale: Ultimo movimento del cliente (CUSTOMER: solo sé stesso)\n- `terminalId` (string) — opzionale: Id terminale numerico, oppure \"All\" per ultimo movimento su qualsiasi terminale (POS: default = terminale del token)\n\n**Risposte di successo:**\n- **200**: Ultimo movimento del programma (filtri opzionali cliente/terminale).\n\n**Errori documentati:** 400, 401, 403, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Movements.","parameters":[{"schema":{"type":"integer"},"in":"query","name":"programId","required":false,"description":"Obbligatorio per backend; per CUSTOMER/POS deriva dal token se omesso"},{"schema":{"type":"integer"},"in":"query","name":"customerId","required":false,"description":"Ultimo movimento del cliente (CUSTOMER: solo sé stesso)"},{"schema":{"type":"string"},"in":"query","name":"terminalId","required":false,"description":"Id terminale numerico, oppure \"All\" per ultimo movimento su qualsiasi terminale (POS: default = terminale del token)"}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Ultimo movimento del programma (filtri opzionali cliente/terminale).","content":{"application/json":{"schema":{"description":"Ultimo movimento del programma (filtri opzionali cliente/terminale).","type":"object","additionalProperties":true,"example":{"id":1001,"kind":1,"dateTime":"2026-05-15T10:00:00.000Z","totalMoney":49.99,"programId":1,"customerId":2,"customerCardId":1,"shopId":1,"terminalId":1,"status":"APPROVED","currency":{"id":1,"code":"EUR","name":"Euro","symbol":"€"},"promosWon":[],"autoChargeWalletTransactions":[],"walletPaymentsApplied":[],"createdAt":"2026-05-15T10:00:00.000Z","cancelWithinMinutes":30}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/customers/{customerId}/movements/last-at-pos":{"get":{"tags":["Movements","Customers"],"description":"Ultimo movimento del cliente sul terminale del token POS (non l'ultimo assoluto del cliente).\n\n**Contesto:** I **Movimenti** sono transazioni del loyalty: vendite, rimborsi, open movement, riscatti. Registrano importi, wallet coinvolti, promo applicate e terminale/negozio.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `customerId` (integer) — obbligatorio: Id cliente\n\n**Risposte di successo:**\n- **200**: Ultimo movimento del cliente sul terminale POS del token (solo operatorType POS).\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Movements.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"customerId","required":true,"description":"Id cliente"}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Ultimo movimento del cliente sul terminale POS del token (solo operatorType POS).","content":{"application/json":{"schema":{"description":"Ultimo movimento del cliente sul terminale POS del token (solo operatorType POS).","type":"object","additionalProperties":true,"example":{"id":1001,"kind":2,"dateTime":"2026-05-15T10:00:00.000Z","totalMoney":49.99,"programId":1,"customerId":2,"customerCardId":1,"shopId":1,"terminalId":208,"status":"APPROVED","currency":{"id":1,"code":"EUR","name":"Euro","symbol":"€"},"promosWon":[],"autoChargeWalletTransactions":[],"walletPaymentsApplied":[],"createdAt":"2026-05-15T10:00:00.000Z","cancelWithinMinutes":30}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/movements/{id}/cancel":{"post":{"tags":["Movements"],"description":"Crea cancel.\n\n**Contesto:** I **Movimenti** sono transazioni del loyalty: vendite, rimborsi, open movement, riscatti. Registrano importi, wallet coinvolti, promo applicate e terminale/negozio.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `id` (integer) — obbligatorio\n\n**Body JSON:**\n- `reason` (string) — opzionale: Motivo annullo (alias legacy)\n- `notes` (string) — opzionale: Note salvate sul movimento di storno\n\n**Risposte di successo:**\n- **201**: Annulla un movimento. Il POS rispetta cancelWithinMinutes. Superadmin e movements:cancel_unrestricted saltano la finestra e richiedono un motivo.\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Movements.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"reason":{"type":"string","description":"Motivo annullo (alias legacy)"},"notes":{"type":"string","description":"Note salvate sul movimento di storno"}}}}}},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"201":{"description":"Annulla un movimento. Il POS rispetta cancelWithinMinutes. Superadmin e movements:cancel_unrestricted saltano la finestra e richiedono un motivo.","content":{"application/json":{"schema":{"description":"Annulla un movimento. Il POS rispetta cancelWithinMinutes. Superadmin e movements:cancel_unrestricted saltano la finestra e richiedono un motivo.","type":"object","additionalProperties":true,"example":{"cancelledMovementId":1001,"cancellationMovementId":1002,"cancelWithinMinutes":30,"walletReversals":[{"originalWalletTransactionId":5001,"reversalWalletTransactionId":5002,"walletId":1,"transactionType":"CHARGE","units":100}],"cancelledMovement":{"id":1001,"kind":1,"status":"CANCELLED","totalMoney":49.99},"cancellationMovement":{"id":1002,"kind":2,"status":"APPROVED","totalMoney":-49.99}}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/movements/{id}":{"get":{"tags":["Movements"],"description":"Dettaglio di movimento per identificativo.\n\n**Contesto:** I **Movimenti** sono transazioni del loyalty: vendite, rimborsi, open movement, riscatti. Registrano importi, wallet coinvolti, promo applicate e terminale/negozio.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `id` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Dettaglio movimento\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Movements.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Dettaglio movimento","content":{"application/json":{"schema":{"description":"Dettaglio movimento","type":"object","additionalProperties":true,"example":{"id":1001,"kind":1,"dateTime":"2026-01-15T10:00:00.000Z","totalMoney":49.99,"programId":1,"customerId":2,"customerCardId":1,"shopId":1,"terminalId":1,"status":"APPROVED","currency":{"id":1,"code":"EUR","name":"Euro","symbol":"€"},"promosWon":[],"autoChargeWalletTransactions":[],"walletPaymentsApplied":[],"createdAt":"2026-01-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/movements/sales":{"post":{"tags":["Movements"],"description":"Crea sales.\n\n**Contesto:** I **Movimenti** sono transazioni del loyalty: vendite, rimborsi, open movement, riscatti. Registrano importi, wallet coinvolti, promo applicate e terminale/negozio.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Body JSON:**\n- `programId` (integer) — obbligatorio\n- `customerId` (integer) — opzionale\n- `shopId` (integer) — opzionale\n- `terminalId` (integer) — opzionale\n- `totalMoney` (number) — obbligatorio\n- `currency` (string) — opzionale: Moneta dell'importo: { id } o currencyId. Se assente, usa la moneta di default del programma (localCurrencyId).\n- `currencyId` (integer) — opzionale: Id moneta (alternativa a currency.id)\n- `products` (array) — opzionale\n- `ticketId` (string) — opzionale: Identificativo ticket (univoco)\n- `foreignId` (string) — opzionale: Identificativo esterno (univoco)\n- `notes` (string) — opzionale: Note opzionali salvate sul movimento\n- `customData` (object) — opzionale\n- `voucherCode` (string) — opzionale: Codice voucher da applicare (sconto scalato dalla spesa)\n- `walletPayments` (array) — opzionale: Unità da scalare da wallet su specifica carta cliente (customerCardId)\n\n**Risposte di successo:**\n- **201**: Vendita registrata\n\n**Errori documentati:** 400, 401, 403, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Movements.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["programId","totalMoney"],"properties":{"programId":{"type":"integer"},"customerId":{"type":"integer"},"shopId":{"type":"integer"},"terminalId":{"type":"integer"},"totalMoney":{"type":"number"},"currency":{"anyOf":[{"type":"object"},{"type":"integer"},{"type":"null"}],"description":"Moneta dell'importo: { id } o currencyId. Se assente, usa la moneta di default del programma (localCurrencyId)."},"currencyId":{"type":"integer","description":"Id moneta (alternativa a currency.id)","nullable":true},"products":{"type":"array","items":{"type":"object","properties":{"productId":{"type":"integer"},"quantity":{"type":"integer"},"unitPrice":{"type":"number"},"totalPrice":{"type":"number"}}}},"ticketId":{"type":"string","description":"Identificativo ticket (univoco)","nullable":true},"foreignId":{"type":"string","description":"Identificativo esterno (univoco)","nullable":true},"notes":{"type":"string","description":"Note opzionali salvate sul movimento","nullable":true},"customData":{"type":"object"},"voucherCode":{"type":"string","description":"Codice voucher da applicare (sconto scalato dalla spesa, salvo sales.voucherNoDiscount). Combinabile con walletPayments solo se businessRules.sales.benefitsStackable è true. Più voucher solo se sales.singleVoucherOnly è false. La somma dei voucher non può superare sales.voucherMaxAmount e/o sales.voucherMaxPercent della spesa (0 = nessun limite; se entrambi sono impostati vale il più basso).","nullable":true},"voucherCodes":{"type":"array","items":{"type":"string"},"description":"Codici voucher da applicare nella stessa vendita. Rifiutato con MULTIPLE_VOUCHERS_NOT_ALLOWED se sales.singleVoucherOnly è true e i codici distinti sono più di uno. Si unisce a voucherCode senza duplicati. La somma dei valori è rifiutata con VOUCHER_USAGE_LIMIT_EXCEEDED se supera il limite in euro e/o in percentuale della spesa."},"walletPayments":{"type":"array","items":{"type":"object","required":["walletId","customerCardId","units"],"properties":{"walletId":{"type":"integer"},"customerCardId":{"type":"integer"},"units":{"type":"number"}}},"description":"Unità da scalare da wallet su specifica carta cliente (customerCardId). Il valore in euro si scala dalla spesa, salvo sales.voucherNoDiscount: in quel caso i punti si scaricano e l'importo da pagare resta quello inserito. Combinabile con voucherCode solo se businessRules.sales.benefitsStackable è true."}}}}},"required":true},"security":[{"bearerAuth":[]}],"responses":{"201":{"description":"Vendita registrata","content":{"application/json":{"schema":{"description":"Vendita registrata","type":"object","additionalProperties":true,"example":{"id":1001,"kind":1,"dateTime":"2026-01-15T10:00:00.000Z","totalMoney":49.99,"programId":1,"customerId":2,"customerCardId":1,"shopId":1,"terminalId":1,"status":"APPROVED","currency":{"id":1,"code":"EUR","name":"Euro","symbol":"€"},"promosWon":[],"autoChargeWalletTransactions":[],"walletPaymentsApplied":[],"createdAt":"2026-01-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/movements/open-movement":{"post":{"tags":["Movements"],"description":"Registra un movimento libero (open movement). Esegue il motore promo con trigger Open Movement.\n\n**Contesto:** I **Movimenti** sono transazioni del loyalty: vendite, rimborsi, open movement, riscatti. Registrano importi, wallet coinvolti, promo applicate e terminale/negozio.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Body JSON:**\n- `programId` (integer) — obbligatorio\n- `customerId` (integer) — opzionale\n- `shopId` (integer) — opzionale\n- `terminalId` (integer) — opzionale\n- `openMovementId` (integer) — obbligatorio\n- `value` (number) — obbligatorio\n- `currency` (string) — opzionale\n- `currencyId` (integer) — opzionale\n\n**Risposte di successo:**\n- **201**: Open movement\n\n**Errori documentati:** 400, 401, 403, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Movements.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["programId","openMovementId","value"],"properties":{"programId":{"type":"integer"},"customerId":{"type":"integer"},"shopId":{"type":"integer"},"terminalId":{"type":"integer"},"openMovementId":{"type":"integer"},"value":{"type":"number"},"currency":{"anyOf":[{"type":"object"},{"type":"integer"},{"type":"null"}]},"currencyId":{"type":"integer","nullable":true}}}}},"required":true},"security":[{"bearerAuth":[]}],"responses":{"201":{"description":"Open movement","content":{"application/json":{"schema":{"description":"Open movement","type":"object","additionalProperties":true,"example":{"id":1003,"kind":50,"totalMoney":25,"promosWon":[],"createdAt":"2026-01-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/movements/refunds":{"post":{"tags":["Movements"],"description":"Crea refunds.\n\n**Contesto:** I **Movimenti** sono transazioni del loyalty: vendite, rimborsi, open movement, riscatti. Registrano importi, wallet coinvolti, promo applicate e terminale/negozio.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Body JSON:**\n- `originalSaleId` (integer) — obbligatorio\n- `customerId` (integer) — opzionale\n- `shopId` (integer) — opzionale\n- `terminalId` (integer) — opzionale\n- `money` (number) — obbligatorio\n- `reason` (string) — opzionale: Motivo rimborso (alias legacy)\n- `notes` (string) — opzionale: Note salvate sul movimento di rimborso\n- `ticketId` (string) — opzionale: Identificativo ticket (univoco)\n- `foreignId` (string) — opzionale: Identificativo esterno (univoco)\n- `currency` (string) — opzionale\n- `currencyId` (integer) — opzionale\n\n**Risposte di successo:**\n- **201**: Rimborso\n\n**Errori documentati:** 400, 401, 403, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Movements.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["originalSaleId","money"],"properties":{"originalSaleId":{"type":"integer"},"customerId":{"type":"integer"},"shopId":{"type":"integer"},"terminalId":{"type":"integer"},"money":{"type":"number"},"reason":{"type":"string","description":"Motivo rimborso (alias legacy)"},"notes":{"type":"string","description":"Note salvate sul movimento di rimborso"},"ticketId":{"type":"string","description":"Identificativo ticket (univoco)","nullable":true},"foreignId":{"type":"string","description":"Identificativo esterno (univoco)","nullable":true},"currency":{"anyOf":[{"type":"object"},{"type":"integer"},{"type":"null"}]},"currencyId":{"type":"integer","nullable":true}}}}},"required":true},"security":[{"bearerAuth":[]}],"responses":{"201":{"description":"Rimborso","content":{"application/json":{"schema":{"description":"Rimborso","type":"object","additionalProperties":true,"example":{"id":1002,"kind":27,"originalSaleId":1001,"totalMoney":-10,"status":"APPROVED","complete":false,"walletReversals":[],"shopWalletReversals":[],"voucherRestored":[],"createdAt":"2026-01-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/movements/giftCardDischarge":{"post":{"tags":["Movements"],"description":"Scarica una gift card del programma al POS. Richiede token POS; la GC deve essere utilizzabile nel negozio del terminale (shop + spend_mode).\n\n**Contesto:** I **Movimenti** sono transazioni del loyalty: vendite, rimborsi, open movement, riscatti. Registrano importi, wallet coinvolti, promo applicate e terminale/negozio.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Body JSON:**\n- `programId` (integer) — obbligatorio\n- `cardCode` (string) — opzionale: Codice gift card digitale (es. GC1234567890123456)\n- `cardNumber` (string) — opzionale: Numero card fisica Gift Card (alternativa a cardCode)\n- `amount` (number) — obbligatorio: Importo da scaricare\n- `shopId` (integer) — opzionale: Negozio; default shopId del token POS\n- `terminalId` (integer) — opzionale: Terminale; default terminalId del token POS\n- `ticketId` (string) — opzionale\n- `foreignId` (string) — opzionale\n- `notes` (string) — opzionale\n\n**Risposte di successo:**\n- **201**: Scarica una gift card emessa/acquistata al POS (token POS, negozio abilitato).\n\n**Errori documentati:** 400, 401, 403, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Movements.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["programId","amount"],"properties":{"programId":{"type":"integer"},"cardCode":{"type":"string","description":"Codice gift card digitale (es. GC1234567890123456)"},"cardNumber":{"type":"string","description":"Numero card fisica Gift Card (alternativa a cardCode)"},"amount":{"type":"number","description":"Importo da scaricare"},"shopId":{"type":"integer","description":"Negozio; default shopId del token POS"},"terminalId":{"type":"integer","description":"Terminale; default terminalId del token POS"},"ticketId":{"type":"string","nullable":true},"foreignId":{"type":"string","nullable":true},"notes":{"type":"string","nullable":true}}}}},"required":true},"security":[{"bearerAuth":[]}],"responses":{"201":{"description":"Scarica una gift card emessa/acquistata al POS (token POS, negozio abilitato).","content":{"application/json":{"schema":{"description":"Scarica una gift card emessa/acquistata al POS (token POS, negozio abilitato).","type":"object","additionalProperties":true,"example":{"id":1004,"kind":69,"dateTime":"2026-05-15T10:00:00.000Z","totalMoney":25,"programId":1,"customerId":2,"customerCardId":1,"shopId":1,"terminalId":1,"status":"APPROVED","giftCardDischarge":{"purchasedId":501,"giftCardId":10,"variantId":3,"cardCode":"GC1234567890123456","giftCardName":"Gift Card Negozio","dischargedAmount":25,"remainingValue":0,"status":"USED"},"createdAt":"2026-05-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/movements/activatePhysicalGiftCard":{"post":{"tags":["Movements"],"description":"Attiva una card fisica di whitelist Gift Card al POS: assegna al cliente (o anonimo) ed emette la GC digitale con variante più vicina al valore configurato.\n\n**Contesto:** I **Movimenti** sono transazioni del loyalty: vendite, rimborsi, open movement, riscatti. Registrano importi, wallet coinvolti, promo applicate e terminale/negozio.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Body JSON:**\n- `programId` (integer) — obbligatorio\n- `cardNumber` (string) — obbligatorio: Numero card fisica in whitelist Gift Card\n- `cardValue` (number) — opzionale: Valore GC da emettere (opzionale; default valore whitelist)\n- `customerId` (integer) — opzionale: Cliente; omitido/0 → cliente anonimo\n- `shopId` (integer) — opzionale: Negozio; default shopId del token POS\n- `terminalId` (integer) — opzionale: Terminale; default terminalId del token POS\n- `ticketId` (string) — opzionale\n- `foreignId` (string) — opzionale\n- `notes` (string) — opzionale\n\n**Risposte di successo:**\n- **201**: Attiva card fisica Gift Card al POS ed emette GC digitale (token POS).\n\n**Errori documentati:** 400, 401, 403, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Movements.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["programId","cardNumber"],"properties":{"programId":{"type":"integer"},"cardNumber":{"type":"string","description":"Numero card fisica in whitelist Gift Card"},"cardValue":{"type":"number","description":"Valore GC da emettere (opzionale; default valore whitelist)"},"customerId":{"type":"integer","description":"Cliente; omitido/0 → cliente anonimo","nullable":true},"shopId":{"type":"integer","description":"Negozio; default shopId del token POS"},"terminalId":{"type":"integer","description":"Terminale; default terminalId del token POS"},"ticketId":{"type":"string","nullable":true},"foreignId":{"type":"string","nullable":true},"notes":{"type":"string","nullable":true}}}}},"required":true},"security":[{"bearerAuth":[]}],"responses":{"201":{"description":"Attiva card fisica Gift Card al POS ed emette GC digitale (token POS).","content":{"application/json":{"schema":{"description":"Attiva card fisica Gift Card al POS ed emette GC digitale (token POS).","type":"object","additionalProperties":true,"example":{"id":1005,"kind":67,"dateTime":"2026-05-15T10:00:00.000Z","totalMoney":50,"cardValue":50,"programId":1,"customerId":42,"customerCardId":99,"shopId":1,"terminalId":1,"status":"APPROVED","activatePhysicalGiftCard":{"cardNumber":"1234567890","anonymousCustomerCreated":false,"targetValue":50,"giftCardId":10,"variantId":3,"giftCardName":"Gift Card Negozio","purchasedId":502,"cardCode":"GC1234567890123456","cardValue":50,"remainingValue":50,"status":"ISSUED"},"createdAt":"2026-05-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/public/receipts":{"get":{"tags":["Movements"],"description":"Scontrino pubblico. Il token in query contiene programma e movimento, firmati.","parameters":[{"schema":{"type":"string"},"in":"query","name":"token","required":true}],"security":[],"responses":{"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/wallets/{walletId}/movements/{movementId}/remove":{"post":{"tags":["Wallets"],"description":"Rimuove un carico wallet (soft-delete + storno saldo, senza nuovo movimento)","parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"walletId","required":true},{"schema":{"type":"integer"},"in":"path","name":"movementId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/promotions":{"get":{"tags":["Promotions"],"description":"Elenco paginato di promozione del programma indicato in `programId`.\n\n**Contesto:** Le **Promozioni** sono regole del motore promo: trigger (movimento, schedulato, webhook), filtri (importo, negozio, audience, tag) e azioni (carico wallet, voucher, cambio tier, badge).\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n\n**Querystring:**\n- `status` (string) — opzionale (valori: INACTIVE, ACTIVE, FINISHED)\n- `page` (integer) — opzionale [default: 1]\n- `size` (integer) — opzionale [default: 20]\n\n**Risposte di successo:**\n- **200**: Lista promotions\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Promotions.","parameters":[{"schema":{"type":"string","enum":["INACTIVE","ACTIVE","FINISHED"]},"in":"query","name":"status","required":false},{"schema":{"type":"integer","default":1},"in":"query","name":"page","required":false},{"schema":{"type":"integer","default":20},"in":"query","name":"size","required":false},{"schema":{"type":"integer"},"in":"path","name":"programId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Lista promotions","content":{"application/json":{"schema":{"description":"Lista promotions","type":"object","additionalProperties":true,"example":{"content":[{"id":1,"programId":1,"name":"Sconto 10%","status":"DRAFT","kind":"DISCOUNT","createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}],"totalElements":1,"totalPages":1,"page":1,"size":20}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"post":{"tags":["Promotions"],"description":"Create promotion. workflowConfig: workflow del configuratore (nodes + connections). Vedi esempio.\n\n**Contesto:** Le **Promozioni** sono regole del motore promo: trigger (movimento, schedulato, webhook), filtri (importo, negozio, audience, tag) e azioni (carico wallet, voucher, cambio tier, badge).\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n\n**Body JSON:**\n- `name` (string) — obbligatorio\n- `description` (string) — opzionale\n- `workflowConfig` (object) — opzionale: Workflow dal frontend martabuilder: nodes (trigger, filter, action, communicate, instantwin, budget, wait) e connections\n- `startDate` (string) — opzionale\n- `endDate` (string) — opzionale\n- `notVisibleToCustomer` (boolean) — opzionale [default: false]\n- `aiConversationId` (string) — opzionale: ID conversazione AI che ha generato la promo. Opzionale.\n- `imageUrls` (array) — opzionale: URL immagini promo (S3); la prima è anteprima\n\n**Risposte di successo:**\n- **201**: Risorsa creata (promotions)\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Promotions.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["name"],"properties":{"name":{"type":"string"},"description":{"type":"string"},"workflowConfig":{"type":"object","description":"Workflow dal frontend martabuilder: nodes (trigger, filter, action, communicate, instantwin, budget, wait) e connections","properties":{"nodes":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"type":{"type":"string","enum":["trigger","filter","action","communicate","instantwin","budget","wait"]},"title":{"type":"string"},"subtitle":{"type":"string"},"x":{"type":"number"},"y":{"type":"number"},"data":{"type":"object"}}}},"connections":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"from":{"type":"string"},"to":{"type":"string"},"type":{"type":"string","enum":["standard","true","false"]}}}}},"example":{"nodes":[{"id":"node-1","type":"trigger","title":"Trigger","subtitle":"Vendita","x":100,"y":250,"data":{"mode":"MOVEMENT","eventKind":2}},{"id":"node-2","type":"filter","title":"Filtro","subtitle":"Condizioni","x":400,"y":250,"data":{"rules":[{"id":"r1","datum":{"name":"Tier Cliente","group":"AUDIENCE"},"values":[{"id":"v1","operator":"Uguale","value":"Gold","logic":"AND"}]}]}},{"id":"node-3","type":"instantwin","title":"Instant Win","subtitle":"Concorso","x":700,"y":250,"data":{"quantity":100,"uniformByShop":true,"uniformByDuration":true}},{"id":"node-4","type":"action","title":"Azione","subtitle":"Wallet: Punti (+100)","x":980,"y":200,"data":{"actionType":"WALLET","walletId":"Punti","walletOperation":"CHARGE","walletValueType":"FIXED","walletValue":100}},{"id":"node-5","type":"communicate","title":"Communica","subtitle":"Email + WhatsApp","x":980,"y":320,"data":{"communication":{"channels":{"email":true,"whatsapp":true},"messages":{"email":{"subject":"Hai vinto!","body":"Complimenti #Nome#!","imageUrl":"https://example.com/win.png"},"whatsapp":{"body":"Hai vinto!","imageUrl":"https://example.com/win.png"}}}}},{"id":"node-6","type":"budget","title":"Budget","subtitle":"Max 1000/Giorno","x":700,"y":420,"data":{"limitQuantity":true,"maxQuantity":1000,"limitFrequency":true,"maxFrequency":1,"frequencyUnit":"DAYS"}}],"connections":[{"id":"conn-1","from":"node-1","to":"node-2","type":"standard"},{"id":"conn-2","from":"node-2","to":"node-3","type":"true"},{"id":"conn-3","from":"node-3","to":"node-4","type":"true"},{"id":"conn-4","from":"node-3","to":"node-5","type":"false"},{"id":"conn-5","from":"node-2","to":"node-6","type":"false"}]}},"startDate":{"type":"string","format":"date-time"},"endDate":{"type":"string","format":"date-time"},"notVisibleToCustomer":{"type":"boolean","default":false},"aiConversationId":{"type":"string","maxLength":255,"nullable":true,"description":"ID conversazione AI che ha generato la promo. Opzionale."},"imageUrls":{"type":"array","items":{"type":"string"},"description":"URL immagini promo (S3); la prima è anteprima"}}}}},"required":true},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"201":{"description":"Risorsa creata (promotions)","content":{"application/json":{"schema":{"description":"Risorsa creata (promotions)","type":"object","additionalProperties":true,"example":{"id":1,"programId":1,"name":"Sconto 10%","status":"DRAFT","kind":"DISCOUNT","createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/promotions/{id}":{"get":{"tags":["Promotions"],"description":"Get promotion details. Include cannotBeModified: true se esiste almeno un nodo instantwin nel workflow con almeno uno slot Instant Win già assegnato (promotion_instant_win_slots.awarded_movement_id).\n\n**Contesto:** Le **Promozioni** sono regole del motore promo: trigger (movimento, schedulato, webhook), filtri (importo, negozio, audience, tag) e azioni (carico wallet, voucher, cambio tier, badge).\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n- `id` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Dettaglio promotions\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Promotions.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Dettaglio promotions","content":{"application/json":{"schema":{"description":"Dettaglio promotions","type":"object","additionalProperties":true,"example":{"id":1,"programId":1,"name":"Sconto 10%","status":"DRAFT","kind":"DISCOUNT","createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"put":{"tags":["Promotions"],"description":"Update promotion (incluso workflowConfig). Se cannotBeModified: solo name e description aggiornabili; startDate/endDate nel body ignorati; 409 se workflow (grafo) o status diversi dal salvato.\n\n**Contesto:** Le **Promozioni** sono regole del motore promo: trigger (movimento, schedulato, webhook), filtri (importo, negozio, audience, tag) e azioni (carico wallet, voucher, cambio tier, badge).\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n- `id` (integer) — obbligatorio\n\n**Body JSON:**\n- `name` (string) — opzionale\n- `description` (string) — opzionale\n- `status` (string) — opzionale (valori: INACTIVE, ACTIVE, FINISHED)\n- `workflowConfig` (object) — opzionale\n- `startDate` (string) — opzionale\n- `endDate` (string) — opzionale\n- `notVisibleToCustomer` (boolean) — opzionale\n- `aiConversationId` (string) — opzionale: ID conversazione AI associata. Passa stringa vuota o null per rimuoverla.\n- `imageUrls` (array) — opzionale: URL immagini promo (S3)\n\n**Risposte di successo:**\n- **200**: Aggiornamento promotions\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Promotions.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"description":{"type":"string"},"status":{"type":"string","enum":["INACTIVE","ACTIVE","FINISHED"]},"workflowConfig":{"type":"object","example":{"nodes":[{"id":"node-1","type":"trigger","title":"Trigger","subtitle":"Vendita","x":100,"y":250,"data":{"mode":"MOVEMENT","eventKind":2}},{"id":"node-2","type":"filter","title":"Filtro","subtitle":"Condizioni","x":400,"y":250,"data":{"rules":[{"id":"r1","datum":{"name":"Tier Cliente","group":"AUDIENCE"},"values":[{"id":"v1","operator":"Uguale","value":"Gold","logic":"AND"}]}]}},{"id":"node-3","type":"instantwin","title":"Instant Win","subtitle":"Concorso","x":700,"y":250,"data":{"quantity":100,"uniformByShop":true,"uniformByDuration":true}},{"id":"node-4","type":"action","title":"Azione","subtitle":"Wallet: Punti (+100)","x":980,"y":200,"data":{"actionType":"WALLET","walletId":"Punti","walletOperation":"CHARGE","walletValueType":"FIXED","walletValue":100}},{"id":"node-5","type":"communicate","title":"Communica","subtitle":"Email + WhatsApp","x":980,"y":320,"data":{"communication":{"channels":{"email":true,"whatsapp":true},"messages":{"email":{"subject":"Hai vinto!","body":"Complimenti #Nome#!","imageUrl":"https://example.com/win.png"},"whatsapp":{"body":"Hai vinto!","imageUrl":"https://example.com/win.png"}}}}},{"id":"node-6","type":"budget","title":"Budget","subtitle":"Max 1000/Giorno","x":700,"y":420,"data":{"limitQuantity":true,"maxQuantity":1000,"limitFrequency":true,"maxFrequency":1,"frequencyUnit":"DAYS"}}],"connections":[{"id":"conn-1","from":"node-1","to":"node-2","type":"standard"},{"id":"conn-2","from":"node-2","to":"node-3","type":"true"},{"id":"conn-3","from":"node-3","to":"node-4","type":"true"},{"id":"conn-4","from":"node-3","to":"node-5","type":"false"},{"id":"conn-5","from":"node-2","to":"node-6","type":"false"}]}},"startDate":{"type":"string","format":"date-time"},"endDate":{"type":"string","format":"date-time"},"notVisibleToCustomer":{"type":"boolean"},"aiConversationId":{"type":"string","maxLength":255,"nullable":true,"description":"ID conversazione AI associata. Passa stringa vuota o null per rimuoverla."},"imageUrls":{"type":"array","items":{"type":"string"},"description":"URL immagini promo (S3)"}}}}}},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Aggiornamento promotions","content":{"application/json":{"schema":{"description":"Aggiornamento promotions","type":"object","additionalProperties":true,"example":{"id":1,"programId":1,"name":"Sconto 10%","status":"DRAFT","kind":"DISCOUNT","createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"delete":{"tags":["Promotions"],"description":"Elimina una programma fedeltà del programma indicato in `programId` (soft delete o `TERMINATED` se vincoli FK).\n\n**Contesto:** Le **Promozioni** sono regole del motore promo: trigger (movimento, schedulato, webhook), filtri (importo, negozio, audience, tag) e azioni (carico wallet, voucher, cambio tier, badge).\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n- `id` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **204**: Operazione riuscita senza corpo di risposta.\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Promotions.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"204":{"description":"Operazione riuscita senza corpo di risposta."},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/promotions/{id}/activate":{"post":{"tags":["Promotions"],"description":"Attiva una activate.\n\n**Contesto:** Le **Promozioni** sono regole del motore promo: trigger (movimento, schedulato, webhook), filtri (importo, negozio, audience, tag) e azioni (carico wallet, voucher, cambio tier, badge).\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n- `id` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Attiva promozione.\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Promotions.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Attiva promozione.","content":{"application/json":{"schema":{"description":"Attiva promozione.","type":"object","additionalProperties":true,"example":{"success":true}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/promotions/{id}/deactivate":{"post":{"tags":["Promotions"],"description":"Disattiva una deactivate.\n\n**Contesto:** Le **Promozioni** sono regole del motore promo: trigger (movimento, schedulato, webhook), filtri (importo, negozio, audience, tag) e azioni (carico wallet, voucher, cambio tier, badge).\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n- `id` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Disattiva promozione.\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Promotions.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Disattiva promozione.","content":{"application/json":{"schema":{"description":"Disattiva promozione.","type":"object","additionalProperties":true,"example":{"success":true}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/promotions/checkFilter":{"put":{"tags":["Promotions"],"description":"Validate a promotion-like payload and return how many customers match its filters today. Accepts the same optional promotion fields as the builder (null dates, status, etc.). Methods: PUT or POST.\n\n**Contesto:** Le **Promozioni** sono regole del motore promo: trigger (movimento, schedulato, webhook), filtri (importo, negozio, audience, tag) e azioni (carico wallet, voucher, cambio tier, badge).\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n\n**Body JSON:**\n- `name` (string) — opzionale\n- `description` (string) — opzionale\n- `workflowConfig` (object) — obbligatorio\n- `startDate` (string) — opzionale\n- `endDate` (string) — opzionale\n- `notVisibleToCustomer` (boolean) — opzionale\n- `status` (string) — opzionale (valori: INACTIVE, ACTIVE, FINISHED)\n- `programId` (integer) — opzionale\n\n**Risposte di successo:**\n- **200**: Number of customers matching the promotion filters today and total active customers in the program.\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Promotions.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["workflowConfig"],"properties":{"name":{"type":"string"},"description":{"type":"string"},"workflowConfig":{"type":"object","example":{"nodes":[{"id":"node-1","type":"trigger","title":"Trigger","subtitle":"Vendita","x":100,"y":250,"data":{"mode":"MOVEMENT","eventKind":2}},{"id":"node-2","type":"filter","title":"Filtro","subtitle":"Condizioni","x":400,"y":250,"data":{"rules":[{"id":"r1","datum":{"name":"Tier Cliente","group":"AUDIENCE"},"values":[{"id":"v1","operator":"Uguale","value":"Gold","logic":"AND"}]}]}},{"id":"node-3","type":"instantwin","title":"Instant Win","subtitle":"Concorso","x":700,"y":250,"data":{"quantity":100,"uniformByShop":true,"uniformByDuration":true}},{"id":"node-4","type":"action","title":"Azione","subtitle":"Wallet: Punti (+100)","x":980,"y":200,"data":{"actionType":"WALLET","walletId":"Punti","walletOperation":"CHARGE","walletValueType":"FIXED","walletValue":100}},{"id":"node-5","type":"communicate","title":"Communica","subtitle":"Email + WhatsApp","x":980,"y":320,"data":{"communication":{"channels":{"email":true,"whatsapp":true},"messages":{"email":{"subject":"Hai vinto!","body":"Complimenti #Nome#!","imageUrl":"https://example.com/win.png"},"whatsapp":{"body":"Hai vinto!","imageUrl":"https://example.com/win.png"}}}}},{"id":"node-6","type":"budget","title":"Budget","subtitle":"Max 1000/Giorno","x":700,"y":420,"data":{"limitQuantity":true,"maxQuantity":1000,"limitFrequency":true,"maxFrequency":1,"frequencyUnit":"DAYS"}}],"connections":[{"id":"conn-1","from":"node-1","to":"node-2","type":"standard"},{"id":"conn-2","from":"node-2","to":"node-3","type":"true"},{"id":"conn-3","from":"node-3","to":"node-4","type":"true"},{"id":"conn-4","from":"node-3","to":"node-5","type":"false"},{"id":"conn-5","from":"node-2","to":"node-6","type":"false"}]}},"startDate":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"endDate":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"notVisibleToCustomer":{"type":"boolean"},"status":{"type":"string","enum":["INACTIVE","ACTIVE","FINISHED"]},"programId":{"type":"integer"}}}}},"required":true},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Number of customers matching the promotion filters today and total active customers in the program.","content":{"application/json":{"schema":{"description":"Number of customers matching the promotion filters today and total active customers in the program.","type":"object","properties":{"customersCount":{"type":"integer","example":147},"totalActiveCustomers":{"type":"integer","example":892}},"required":["customersCount","totalActiveCustomers"],"example":{"customersCount":147,"totalActiveCustomers":892}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"post":{"tags":["Promotions"],"description":"Validate a promotion-like payload and return how many customers match its filters today. Accepts the same optional promotion fields as the builder (null dates, status, etc.). Methods: PUT or POST.\n\n**Contesto:** Le **Promozioni** sono regole del motore promo: trigger (movimento, schedulato, webhook), filtri (importo, negozio, audience, tag) e azioni (carico wallet, voucher, cambio tier, badge).\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n\n**Body JSON:**\n- `name` (string) — opzionale\n- `description` (string) — opzionale\n- `workflowConfig` (object) — obbligatorio\n- `startDate` (string) — opzionale\n- `endDate` (string) — opzionale\n- `notVisibleToCustomer` (boolean) — opzionale\n- `status` (string) — opzionale (valori: INACTIVE, ACTIVE, FINISHED)\n- `programId` (integer) — opzionale\n\n**Risposte di successo:**\n- **200**: Number of customers matching the promotion filters today and total active customers in the program.\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Promotions.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["workflowConfig"],"properties":{"name":{"type":"string"},"description":{"type":"string"},"workflowConfig":{"type":"object","example":{"nodes":[{"id":"node-1","type":"trigger","title":"Trigger","subtitle":"Vendita","x":100,"y":250,"data":{"mode":"MOVEMENT","eventKind":2}},{"id":"node-2","type":"filter","title":"Filtro","subtitle":"Condizioni","x":400,"y":250,"data":{"rules":[{"id":"r1","datum":{"name":"Tier Cliente","group":"AUDIENCE"},"values":[{"id":"v1","operator":"Uguale","value":"Gold","logic":"AND"}]}]}},{"id":"node-3","type":"instantwin","title":"Instant Win","subtitle":"Concorso","x":700,"y":250,"data":{"quantity":100,"uniformByShop":true,"uniformByDuration":true}},{"id":"node-4","type":"action","title":"Azione","subtitle":"Wallet: Punti (+100)","x":980,"y":200,"data":{"actionType":"WALLET","walletId":"Punti","walletOperation":"CHARGE","walletValueType":"FIXED","walletValue":100}},{"id":"node-5","type":"communicate","title":"Communica","subtitle":"Email + WhatsApp","x":980,"y":320,"data":{"communication":{"channels":{"email":true,"whatsapp":true},"messages":{"email":{"subject":"Hai vinto!","body":"Complimenti #Nome#!","imageUrl":"https://example.com/win.png"},"whatsapp":{"body":"Hai vinto!","imageUrl":"https://example.com/win.png"}}}}},{"id":"node-6","type":"budget","title":"Budget","subtitle":"Max 1000/Giorno","x":700,"y":420,"data":{"limitQuantity":true,"maxQuantity":1000,"limitFrequency":true,"maxFrequency":1,"frequencyUnit":"DAYS"}}],"connections":[{"id":"conn-1","from":"node-1","to":"node-2","type":"standard"},{"id":"conn-2","from":"node-2","to":"node-3","type":"true"},{"id":"conn-3","from":"node-3","to":"node-4","type":"true"},{"id":"conn-4","from":"node-3","to":"node-5","type":"false"},{"id":"conn-5","from":"node-2","to":"node-6","type":"false"}]}},"startDate":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"endDate":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"notVisibleToCustomer":{"type":"boolean"},"status":{"type":"string","enum":["INACTIVE","ACTIVE","FINISHED"]},"programId":{"type":"integer"}}}}},"required":true},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Number of customers matching the promotion filters today and total active customers in the program.","content":{"application/json":{"schema":{"description":"Number of customers matching the promotion filters today and total active customers in the program.","type":"object","properties":{"customersCount":{"type":"integer","example":147},"totalActiveCustomers":{"type":"integer","example":892}},"required":["customersCount","totalActiveCustomers"],"example":{"customersCount":147,"totalActiveCustomers":892}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/wallets":{"get":{"tags":["Wallets"],"description":"Elenco paginato di wallet del programma indicato in `programId`.\n\n**Contesto:** I **Wallet** sono contenitori di valore configurabili (es. Punti, Visite, Gift Card). Sostituiscono i vecchi punti/crediti fissi: ogni wallet ha simbolo, decimali, livelli e regole di carico/scarico per tier.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n\n**Querystring:**\n- `page` (integer) — opzionale [default: 1]\n- `size` (integer) — opzionale [default: 20]\n\n**Risposte di successo:**\n- **200**: Lista wallets\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Wallets.","parameters":[{"schema":{"type":"integer","minimum":1,"default":1},"in":"query","name":"page","required":false},{"schema":{"type":"integer","minimum":1,"maximum":500,"default":20},"in":"query","name":"size","required":false},{"schema":{"type":"integer"},"in":"path","name":"programId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Lista wallets","content":{"application/json":{"schema":{"description":"Lista wallets","type":"object","additionalProperties":true,"example":{"content":[{"id":1,"name":"Punti","symbol":"PT","description":"Punti loyalty","decimalPlaces":2,"useCardContainers":false,"minBalance":0,"maxBalance":null,"maxDischargePercent":null,"levels":[{"threshold":0,"label":"Principiante"},{"threshold":1000,"label":"Giocatore"},{"threshold":3000,"label":"Esperto"},{"threshold":6000,"label":"Fuoriclasse"},{"threshold":10000,"label":"Leggenda"}],"tierSettings":[{"tierId":6,"expirationDays":null,"chargeUnitsPerAmount":1,"chargeAmountPerUnit":1,"chargeCurrencyId":1,"dischargeAmountPerUnit":0.01,"dischargeCurrencyId":1,"maxDischargePercent":50},{"tierId":5,"expirationDays":null,"chargeUnitsPerAmount":1.1,"chargeAmountPerUnit":1,"chargeCurrencyId":1,"dischargeAmountPerUnit":0.01,"dischargeCurrencyId":1}],"tierAccess":[{"tierId":5,"canCharge":true,"canDischarge":true,"canTransfer":false,"canView":true}],"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}],"totalElements":1,"totalPages":1,"page":1,"size":20}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"post":{"tags":["Wallets"],"description":"Crea wallet del programma indicato in `programId`.\n\n**Contesto:** I **Wallet** sono contenitori di valore configurabili (es. Punti, Visite, Gift Card). Sostituiscono i vecchi punti/crediti fissi: ogni wallet ha simbolo, decimali, livelli e regole di carico/scarico per tier.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n\n**Body JSON:**\n- `name` (string) — obbligatorio\n- `symbol` (string) — opzionale: Simbolo unità (es. €). Non univoco.\n- `code` (string) — opzionale: Deprecato: usare symbol\n- `description` (string) — opzionale\n- `decimalPlaces` (integer) — opzionale [default: 2]\n- `useCardContainers` (boolean) — opzionale: Se true, saldo wallet separato per card; se false, contenitore condiviso customer-wide [default: false]\n- `hiddenInShop` (boolean) — opzionale: Se true, wallet non esposto al POS (negozio) [default: false]\n- `minBalance` (number) — opzionale [default: 0]\n- `maxBalance` (number) — opzionale\n- `expirationDays` (integer) — opzionale\n- `chargeUnitsPerAmount` (number) — opzionale\n- `chargeAmountPerUnit` (number) — opzionale\n- `chargeCurrencyId` (integer) — opzionale\n- `dischargeAmountPerUnit` (number) — opzionale\n- `dischargeCurrencyId` (integer) — opzionale\n- `dischargeTiers` (array) — opzionale: Soglie scarico: [{ units, amount }]. Ultima soglia scaricabile in multipli.\n- `levels` (array) — opzionale: Soglie livello: [{ threshold, label }]. Es: 0=Principiante, 1000=Giocatore, 3000=Esperto\n- `tierSettings` (array) — opzionale\n- `tierAccess` (array) — opzionale\n\n**Risposte di successo:**\n- **201**: Risorsa creata (wallets)\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Wallets.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["name"],"properties":{"name":{"type":"string"},"symbol":{"type":"string","description":"Simbolo unità (es. €). Non univoco."},"code":{"type":"string","description":"Deprecato: usare symbol"},"description":{"type":"string"},"decimalPlaces":{"type":"integer","default":2,"minimum":0,"maximum":8},"useCardContainers":{"type":"boolean","default":false,"description":"Se true, saldo wallet separato per card; se false, contenitore condiviso customer-wide"},"hiddenInShop":{"type":"boolean","default":false,"description":"Se true, wallet non esposto al POS (negozio)"},"minBalance":{"type":"number","default":0},"maxBalance":{"type":"number","nullable":true},"expirationDays":{"type":"integer","nullable":true},"chargeUnitsPerAmount":{"type":"number","nullable":true},"chargeAmountPerUnit":{"type":"number","nullable":true},"chargeCurrencyId":{"type":"integer","nullable":true},"dischargeAmountPerUnit":{"type":"number","nullable":true},"dischargeCurrencyId":{"type":"integer","nullable":true},"maxDischargePercent":{"type":"number","minimum":0,"maximum":100,"description":"Percentuale massima della spesa pagabile con questo wallet. Null = 100%.","nullable":true},"dischargeTiers":{"type":"array","description":"Soglie scarico: [{ units, amount }]. Ultima soglia scaricabile in multipli.","items":{"type":"object","required":["units","amount"],"properties":{"units":{"type":"number","exclusiveMinimum":0},"amount":{"type":"number","exclusiveMinimum":0}}}},"levels":{"type":"array","description":"Soglie livello: [{ threshold, label }]. Es: 0=Principiante, 1000=Giocatore, 3000=Esperto","items":{"type":"object","required":["threshold","label"],"properties":{"threshold":{"type":"number","minimum":0},"label":{"type":"string"}}}},"tierSettings":{"type":"array","items":{"type":"object","required":["tierId"],"properties":{"tierId":{"type":"integer"},"expirationDays":{"type":"integer","nullable":true},"chargeUnitsPerAmount":{"type":"number","nullable":true},"chargeAmountPerUnit":{"type":"number","nullable":true},"chargeCurrencyId":{"type":"integer","nullable":true},"dischargeAmountPerUnit":{"type":"number","nullable":true},"dischargeCurrencyId":{"type":"integer","nullable":true},"maxDischargePercent":{"type":"number","minimum":0,"maximum":100,"description":"Percentuale massima della spesa per questo tier. Null = default del wallet, oppure 100%.","nullable":true},"dischargeTiers":{"type":"array","items":{"type":"object","required":["units","amount"],"properties":{"units":{"type":"number","exclusiveMinimum":0},"amount":{"type":"number","exclusiveMinimum":0}}}}}}},"tierAccess":{"type":"array","items":{"type":"object","required":["tierId"],"properties":{"tierId":{"type":"integer"},"canCharge":{"type":"boolean","default":true},"canDischarge":{"type":"boolean","default":true},"canTransfer":{"type":"boolean","default":false},"canView":{"type":"boolean","default":true}}}}}}}},"required":true},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"201":{"description":"Risorsa creata (wallets)","content":{"application/json":{"schema":{"description":"Risorsa creata (wallets)","type":"object","additionalProperties":true,"example":{"id":1,"name":"Punti","symbol":"PT","description":"Punti loyalty","decimalPlaces":2,"useCardContainers":false,"minBalance":0,"maxBalance":null,"maxDischargePercent":null,"levels":[{"threshold":0,"label":"Principiante"},{"threshold":1000,"label":"Giocatore"},{"threshold":3000,"label":"Esperto"},{"threshold":6000,"label":"Fuoriclasse"},{"threshold":10000,"label":"Leggenda"}],"tierSettings":[{"tierId":6,"expirationDays":null,"chargeUnitsPerAmount":1,"chargeAmountPerUnit":1,"chargeCurrencyId":1,"dischargeAmountPerUnit":0.01,"dischargeCurrencyId":1,"maxDischargePercent":50},{"tierId":5,"expirationDays":null,"chargeUnitsPerAmount":1.1,"chargeAmountPerUnit":1,"chargeCurrencyId":1,"dischargeAmountPerUnit":0.01,"dischargeCurrencyId":1}],"tierAccess":[{"tierId":5,"canCharge":true,"canDischarge":true,"canTransfer":false,"canView":true}],"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/wallets/{walletId}":{"get":{"tags":["Wallets"],"description":"Dettaglio di una programma fedeltà per identificativo del programma indicato in `programId`.\n\n**Contesto:** I **Wallet** sono contenitori di valore configurabili (es. Punti, Visite, Gift Card). Sostituiscono i vecchi punti/crediti fissi: ogni wallet ha simbolo, decimali, livelli e regole di carico/scarico per tier.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n- `walletId` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Dettaglio wallets\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Wallets.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"walletId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Dettaglio wallets","content":{"application/json":{"schema":{"description":"Dettaglio wallets","type":"object","additionalProperties":true,"example":{"id":1,"name":"Punti","symbol":"PT","description":"Punti loyalty","decimalPlaces":2,"useCardContainers":false,"minBalance":0,"maxBalance":null,"maxDischargePercent":null,"levels":[{"threshold":0,"label":"Principiante"},{"threshold":1000,"label":"Giocatore"},{"threshold":3000,"label":"Esperto"},{"threshold":6000,"label":"Fuoriclasse"},{"threshold":10000,"label":"Leggenda"}],"tierSettings":[{"tierId":6,"expirationDays":null,"chargeUnitsPerAmount":1,"chargeAmountPerUnit":1,"chargeCurrencyId":1,"dischargeAmountPerUnit":0.01,"dischargeCurrencyId":1,"maxDischargePercent":50},{"tierId":5,"expirationDays":null,"chargeUnitsPerAmount":1.1,"chargeAmountPerUnit":1,"chargeCurrencyId":1,"dischargeAmountPerUnit":0.01,"dischargeCurrencyId":1}],"tierAccess":[{"tierId":5,"canCharge":true,"canDischarge":true,"canTransfer":false,"canView":true}],"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"put":{"tags":["Wallets"],"description":"Aggiorna una programma fedeltà (campi consentiti) del programma indicato in `programId`.\n\n**Contesto:** I **Wallet** sono contenitori di valore configurabili (es. Punti, Visite, Gift Card). Sostituiscono i vecchi punti/crediti fissi: ogni wallet ha simbolo, decimali, livelli e regole di carico/scarico per tier.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n- `walletId` (integer) — obbligatorio\n\n**Body JSON:**\n- `name` (string) — opzionale\n- `symbol` (string) — opzionale: Simbolo unità (es. €). Non univoco.\n- `code` (string) — opzionale: Deprecato: usare symbol\n- `description` (string) — opzionale\n- `decimalPlaces` (integer) — opzionale\n- `useCardContainers` (boolean) — opzionale\n- `hiddenInShop` (boolean) — opzionale\n- `minBalance` (number) — opzionale\n- `maxBalance` (number) — opzionale\n- `expirationDays` (integer) — opzionale\n- `chargeUnitsPerAmount` (number) — opzionale\n- `chargeAmountPerUnit` (number) — opzionale\n- `chargeCurrencyId` (integer) — opzionale\n- `dischargeAmountPerUnit` (number) — opzionale\n- `dischargeCurrencyId` (integer) — opzionale\n- `dischargeTiers` (array) — opzionale: Soglie scarico: [{ units, amount }]. Ultima soglia scaricabile in multipli.\n- `levels` (array) — opzionale: Soglie livello: [{ threshold, label }]\n- `tierSettings` (array) — opzionale\n- `tierAccess` (array) — opzionale\n\n**Risposte di successo:**\n- **200**: Aggiornamento wallets\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Wallets.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"symbol":{"type":"string","description":"Simbolo unità (es. €). Non univoco."},"code":{"type":"string","description":"Deprecato: usare symbol"},"description":{"type":"string"},"decimalPlaces":{"type":"integer","minimum":0,"maximum":8},"useCardContainers":{"type":"boolean"},"hiddenInShop":{"type":"boolean"},"minBalance":{"type":"number"},"maxBalance":{"type":"number","nullable":true},"expirationDays":{"type":"integer","nullable":true},"chargeUnitsPerAmount":{"type":"number","nullable":true},"chargeAmountPerUnit":{"type":"number","nullable":true},"chargeCurrencyId":{"type":"integer","nullable":true},"dischargeAmountPerUnit":{"type":"number","nullable":true},"dischargeCurrencyId":{"type":"integer","nullable":true},"maxDischargePercent":{"type":"number","minimum":0,"maximum":100,"description":"Percentuale massima della spesa pagabile con questo wallet. Null = 100%.","nullable":true},"dischargeTiers":{"type":"array","description":"Soglie scarico: [{ units, amount }]. Ultima soglia scaricabile in multipli.","items":{"type":"object","required":["units","amount"],"properties":{"units":{"type":"number","exclusiveMinimum":0},"amount":{"type":"number","exclusiveMinimum":0}}}},"levels":{"type":"array","description":"Soglie livello: [{ threshold, label }]","items":{"type":"object","required":["threshold","label"],"properties":{"threshold":{"type":"number","minimum":0},"label":{"type":"string"}}}},"tierSettings":{"type":"array","items":{"type":"object","required":["tierId"],"properties":{"tierId":{"type":"integer"},"expirationDays":{"type":"integer","nullable":true},"chargeUnitsPerAmount":{"type":"number","nullable":true},"chargeAmountPerUnit":{"type":"number","nullable":true},"chargeCurrencyId":{"type":"integer","nullable":true},"dischargeAmountPerUnit":{"type":"number","nullable":true},"dischargeCurrencyId":{"type":"integer","nullable":true},"maxDischargePercent":{"type":"number","minimum":0,"maximum":100,"description":"Percentuale massima della spesa per questo tier. Null = default del wallet, oppure 100%.","nullable":true},"dischargeTiers":{"type":"array","items":{"type":"object","required":["units","amount"],"properties":{"units":{"type":"number","exclusiveMinimum":0},"amount":{"type":"number","exclusiveMinimum":0}}}}}}},"tierAccess":{"type":"array","items":{"type":"object","required":["tierId"],"properties":{"tierId":{"type":"integer"},"canCharge":{"type":"boolean","default":true},"canDischarge":{"type":"boolean","default":true},"canTransfer":{"type":"boolean","default":false},"canView":{"type":"boolean","default":true}}}}}}}}},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"walletId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Aggiornamento wallets","content":{"application/json":{"schema":{"description":"Aggiornamento wallets","type":"object","additionalProperties":true,"example":{"id":1,"name":"Punti","symbol":"PT","description":"Punti loyalty","decimalPlaces":2,"useCardContainers":false,"minBalance":0,"maxBalance":null,"maxDischargePercent":null,"levels":[{"threshold":0,"label":"Principiante"},{"threshold":1000,"label":"Giocatore"},{"threshold":3000,"label":"Esperto"},{"threshold":6000,"label":"Fuoriclasse"},{"threshold":10000,"label":"Leggenda"}],"tierSettings":[{"tierId":6,"expirationDays":null,"chargeUnitsPerAmount":1,"chargeAmountPerUnit":1,"chargeCurrencyId":1,"dischargeAmountPerUnit":0.01,"dischargeCurrencyId":1,"maxDischargePercent":50},{"tierId":5,"expirationDays":null,"chargeUnitsPerAmount":1.1,"chargeAmountPerUnit":1,"chargeCurrencyId":1,"dischargeAmountPerUnit":0.01,"dischargeCurrencyId":1}],"tierAccess":[{"tierId":5,"canCharge":true,"canDischarge":true,"canTransfer":false,"canView":true}],"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"delete":{"tags":["Wallets"],"description":"Elimina una programma fedeltà del programma indicato in `programId` (soft delete o `TERMINATED` se vincoli FK).\n\n**Contesto:** I **Wallet** sono contenitori di valore configurabili (es. Punti, Visite, Gift Card). Sostituiscono i vecchi punti/crediti fissi: ogni wallet ha simbolo, decimali, livelli e regole di carico/scarico per tier.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n- `walletId` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **204**: Operazione riuscita senza corpo di risposta.\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Wallets.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"walletId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"204":{"description":"Operazione riuscita senza corpo di risposta."},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/wallets/{walletId}/charge":{"post":{"tags":["Wallets"],"description":"Manual charge: add units to customer wallet balance. Con token POS il movimento registra terminalId del token.\n\n**Contesto:** I **Wallet** sono contenitori di valore configurabili (es. Punti, Visite, Gift Card). Sostituiscono i vecchi punti/crediti fissi: ogni wallet ha simbolo, decimali, livelli e regole di carico/scarico per tier.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n- `walletId` (integer) — obbligatorio\n\n**Body JSON:**\n- `customerId` (integer) — obbligatorio\n- `customerCardId` (integer) — opzionale\n- `cardNumber` (string) — opzionale: Alternative a customerCardId: numero carta (customer_cards.card_number)\n- `shopId` (integer) — opzionale: Negozio che eroga il carico (borsellino negozi). Se omesso (es. carico da admin), il saldo cliente viene accreditato senza scalare il borsellino negozio.\n- `units` (number) — obbligatorio\n- `notes` (string) — opzionale: Note opzionali salvate sul movimento\n\n**Risposte di successo:**\n- **200**: Carico manuale unità (HTTP 200)\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Wallets.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["customerId","units"],"properties":{"customerId":{"type":"integer"},"customerCardId":{"type":"integer"},"cardNumber":{"type":"string","description":"Alternative a customerCardId: numero carta (customer_cards.card_number)"},"shopId":{"type":"integer","description":"Negozio che eroga il carico (borsellino negozi). Se omesso (es. carico da admin), il saldo cliente viene accreditato senza scalare il borsellino negozio."},"units":{"type":"number","minimum":0.000001},"notes":{"type":"string","description":"Note opzionali salvate sul movimento"}}}}},"required":true},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"walletId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Carico manuale unità (HTTP 200)","content":{"application/json":{"schema":{"description":"Carico manuale unità (HTTP 200)","type":"object","additionalProperties":true,"example":{"movementId":1001,"walletId":1,"customerCardId":12,"units":100,"balanceAfter":250.5}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/wallets/{walletId}/discharge":{"post":{"tags":["Wallets"],"description":"Manual discharge: subtract units from customer wallet balance. Con token POS il movimento registra terminalId del token.\n\n**Contesto:** I **Wallet** sono contenitori di valore configurabili (es. Punti, Visite, Gift Card). Sostituiscono i vecchi punti/crediti fissi: ogni wallet ha simbolo, decimali, livelli e regole di carico/scarico per tier.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n- `walletId` (integer) — obbligatorio\n\n**Body JSON:**\n- `customerId` (integer) — obbligatorio\n- `customerCardId` (integer) — opzionale\n- `cardNumber` (string) — opzionale: Alternative a customerCardId: numero carta (customer_cards.card_number)\n- `units` (number) — obbligatorio\n- `notes` (string) — opzionale: Note opzionali salvate sul movimento\n\n**Risposte di successo:**\n- **200**: Scarico manuale unità\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Wallets.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["customerId","units"],"properties":{"customerId":{"type":"integer"},"customerCardId":{"type":"integer"},"cardNumber":{"type":"string","description":"Alternative a customerCardId: numero carta (customer_cards.card_number)"},"shopId":{"type":"integer","description":"Negozio che effettua lo scarico (stesso contesto del carico diretto POS). Se omesso con token POS si usa shopId del token."},"units":{"type":"number","minimum":0.000001},"notes":{"type":"string","description":"Note opzionali salvate sul movimento"}}}}},"required":true},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"walletId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Scarico manuale unità","content":{"application/json":{"schema":{"description":"Scarico manuale unità","type":"object","additionalProperties":true,"example":{"movementId":1002,"walletId":1,"customerCardId":12,"units":50,"balanceAfter":200.5}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/tiers":{"get":{"tags":["Tiers"],"description":"Elenco paginato di tier (livello/fascia cliente) del programma indicato in `programId`.\n\n**Contesto:** I **Tier** (livelli/fasce cliente) classificano i clienti (es. Base, Silver, Gold). Hanno livello numerico, soglie di punti/spesa e regolano l'accesso ai wallet. Il cliente può cambiare tier via promo o assegnazione manuale se consentito.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n\n**Querystring:**\n- `page` (integer) — opzionale [default: 1]\n- `size` (integer) — opzionale [default: 20]\n\n**Risposte di successo:**\n- **200**: Lista tiers\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Tiers.","parameters":[{"schema":{"type":"integer","minimum":1,"default":1},"in":"query","name":"page","required":false},{"schema":{"type":"integer","minimum":1,"maximum":500,"default":20},"in":"query","name":"size","required":false},{"schema":{"type":"integer"},"in":"path","name":"programId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Lista tiers","content":{"application/json":{"schema":{"description":"Lista tiers","type":"object","additionalProperties":true,"example":{"content":[{"id":1,"programId":1,"name":"Gold","description":null,"level":3,"minPoints":0,"minSpent":0,"assignableToCustomer":true,"color":"#C9A227","iconPath":null,"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}],"totalElements":1,"totalPages":1,"page":1,"size":20}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"post":{"tags":["Tiers"],"description":"Crea tier (livello/fascia cliente) del programma indicato in `programId`.\n\n**Contesto:** I **Tier** (livelli/fasce cliente) classificano i clienti (es. Base, Silver, Gold). Hanno livello numerico, soglie di punti/spesa e regolano l'accesso ai wallet. Il cliente può cambiare tier via promo o assegnazione manuale se consentito.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n\n**Body JSON:**\n- `name` (string) — obbligatorio\n- `description` (string) — opzionale\n- `level` (integer) — opzionale\n- `parentId` (integer) — opzionale\n- `minPoints` (number) — opzionale\n- `minSpent` (number) — opzionale\n- `assignableToCustomer` (boolean) — opzionale [default: false]\n- `color` (string) — opzionale\n- `iconPath` (string) — opzionale\n\n**Risposte di successo:**\n- **201**: Risorsa creata (tiers)\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Tiers.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["name"],"properties":{"name":{"type":"string"},"description":{"type":"string"},"level":{"type":"integer","minimum":1},"parentId":{"type":"integer","nullable":true},"minPoints":{"type":"number"},"minSpent":{"type":"number"},"assignableToCustomer":{"type":"boolean","default":false},"color":{"type":"string"},"iconPath":{"type":"string"}}}}},"required":true},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"201":{"description":"Risorsa creata (tiers)","content":{"application/json":{"schema":{"description":"Risorsa creata (tiers)","type":"object","additionalProperties":true,"example":{"id":1,"programId":1,"name":"Gold","description":null,"level":3,"minPoints":0,"minSpent":0,"assignableToCustomer":true,"color":"#C9A227","iconPath":null,"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/tiers/{tierId}":{"get":{"tags":["Tiers"],"description":"Dettaglio di una programma fedeltà per identificativo del programma indicato in `programId`.\n\n**Contesto:** I **Tier** (livelli/fasce cliente) classificano i clienti (es. Base, Silver, Gold). Hanno livello numerico, soglie di punti/spesa e regolano l'accesso ai wallet. Il cliente può cambiare tier via promo o assegnazione manuale se consentito.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n- `tierId` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Dettaglio tiers\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Tiers.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"tierId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Dettaglio tiers","content":{"application/json":{"schema":{"description":"Dettaglio tiers","type":"object","additionalProperties":true,"example":{"id":1,"programId":1,"name":"Gold","description":null,"level":3,"minPoints":0,"minSpent":0,"assignableToCustomer":true,"color":"#C9A227","iconPath":null,"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"put":{"tags":["Tiers"],"description":"Aggiorna una programma fedeltà (campi consentiti) del programma indicato in `programId`.\n\n**Contesto:** I **Tier** (livelli/fasce cliente) classificano i clienti (es. Base, Silver, Gold). Hanno livello numerico, soglie di punti/spesa e regolano l'accesso ai wallet. Il cliente può cambiare tier via promo o assegnazione manuale se consentito.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n- `tierId` (integer) — obbligatorio\n\n**Body JSON:**\n- `name` (string) — opzionale\n- `description` (string) — opzionale\n- `level` (integer) — opzionale\n- `minPoints` (number) — opzionale\n- `minSpent` (number) — opzionale\n- `assignableToCustomer` (boolean) — opzionale\n- `color` (string) — opzionale\n- `iconPath` (string) — opzionale\n\n**Risposte di successo:**\n- **200**: Aggiornamento tiers\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Tiers.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"description":{"type":"string"},"level":{"type":"integer","minimum":1},"minPoints":{"type":"number"},"minSpent":{"type":"number"},"assignableToCustomer":{"type":"boolean"},"color":{"type":"string","nullable":true},"iconPath":{"type":"string","nullable":true}}}}}},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"tierId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Aggiornamento tiers","content":{"application/json":{"schema":{"description":"Aggiornamento tiers","type":"object","additionalProperties":true,"example":{"id":1,"programId":1,"name":"Gold","description":null,"level":3,"minPoints":0,"minSpent":0,"assignableToCustomer":true,"color":"#C9A227","iconPath":null,"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"delete":{"tags":["Tiers"],"description":"Elimina una programma fedeltà del programma indicato in `programId` (soft delete o `TERMINATED` se vincoli FK).\n\n**Contesto:** I **Tier** (livelli/fasce cliente) classificano i clienti (es. Base, Silver, Gold). Hanno livello numerico, soglie di punti/spesa e regolano l'accesso ai wallet. Il cliente può cambiare tier via promo o assegnazione manuale se consentito.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n- `tierId` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **204**: Operazione riuscita senza corpo di risposta.\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Tiers.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"tierId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"204":{"description":"Operazione riuscita senza corpo di risposta."},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/tiers/{tierId}/customers/count":{"get":{"tags":["Tiers"],"description":"Elenco paginato di count del programma indicato in `programId`.\n\n**Contesto:** I **Tier** (livelli/fasce cliente) classificano i clienti (es. Base, Silver, Gold). Hanno livello numerico, soglie di punti/spesa e regolano l'accesso ai wallet. Il cliente può cambiare tier via promo o assegnazione manuale se consentito.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n- `tierId` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Conteggio clienti nel tier.\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Tiers.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"tierId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Conteggio clienti nel tier.","content":{"application/json":{"schema":{"description":"Conteggio clienti nel tier.","type":"object","additionalProperties":true,"example":{"count":120}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/tags":{"get":{"tags":["Tags"],"description":"Elenco paginato di tag del programma indicato in `programId`.\n\n**Contesto:** I **Tag** segmentano clienti e/o negozi (es. VIP, Promo Estate). Si usano in filtri promo, comunicazioni e reportistica.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n\n**Querystring:**\n- `tagType` (string) — opzionale (valori: customers, shops)\n- `page` (integer) — opzionale [default: 1]\n- `size` (integer) — opzionale [default: 20]\n\n**Risposte di successo:**\n- **200**: Lista tags\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Tags.","parameters":[{"schema":{"type":"string","enum":["customers","shops"]},"in":"query","name":"tagType","required":false},{"schema":{"type":"integer","minimum":1,"default":1},"in":"query","name":"page","required":false},{"schema":{"type":"integer","minimum":1,"maximum":500,"default":20},"in":"query","name":"size","required":false},{"schema":{"type":"integer"},"in":"path","name":"programId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Lista tags","content":{"application/json":{"schema":{"description":"Lista tags","type":"object","additionalProperties":true,"example":{"content":[{"id":1,"programId":1,"name":"VIP","description":null,"tagType":"customers","color":"green","iconPath":null,"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}],"totalElements":1,"totalPages":1,"page":1,"size":20}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"post":{"tags":["Tags"],"description":"Crea tag del programma indicato in `programId`.\n\n**Contesto:** I **Tag** segmentano clienti e/o negozi (es. VIP, Promo Estate). Si usano in filtri promo, comunicazioni e reportistica.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n\n**Body JSON:**\n- `name` (string) — obbligatorio\n- `description` (string) — opzionale\n- `tagType` (string) — obbligatorio (valori: customers, shops)\n- `color` (string) — opzionale\n- `iconPath` (string) — opzionale\n\n**Risposte di successo:**\n- **201**: Risorsa creata (tags)\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Tags.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["name","tagType"],"properties":{"name":{"type":"string"},"description":{"type":"string"},"tagType":{"type":"string","enum":["customers","shops"]},"color":{"type":"string","nullable":true},"iconPath":{"type":"string","nullable":true}}}}},"required":true},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"201":{"description":"Risorsa creata (tags)","content":{"application/json":{"schema":{"description":"Risorsa creata (tags)","type":"object","additionalProperties":true,"example":{"id":1,"programId":1,"name":"VIP","description":null,"tagType":"customers","color":"green","iconPath":null,"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/tags/{tagId}":{"get":{"tags":["Tags"],"description":"Dettaglio di una programma fedeltà per identificativo del programma indicato in `programId`.\n\n**Contesto:** I **Tag** segmentano clienti e/o negozi (es. VIP, Promo Estate). Si usano in filtri promo, comunicazioni e reportistica.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n- `tagId` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Dettaglio tags\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Tags.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"tagId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Dettaglio tags","content":{"application/json":{"schema":{"description":"Dettaglio tags","type":"object","additionalProperties":true,"example":{"id":1,"programId":1,"name":"VIP","description":null,"tagType":"customers","color":"green","iconPath":null,"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"put":{"tags":["Tags"],"description":"Aggiorna una programma fedeltà (campi consentiti) del programma indicato in `programId`.\n\n**Contesto:** I **Tag** segmentano clienti e/o negozi (es. VIP, Promo Estate). Si usano in filtri promo, comunicazioni e reportistica.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n- `tagId` (integer) — obbligatorio\n\n**Body JSON:**\n- `name` (string) — opzionale\n- `description` (string) — opzionale\n- `color` (string) — opzionale\n- `iconPath` (string) — opzionale\n\n**Risposte di successo:**\n- **200**: Aggiornamento tags\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Tags.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"description":{"type":"string"},"color":{"type":"string","nullable":true},"iconPath":{"type":"string","nullable":true}}}}}},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"tagId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Aggiornamento tags","content":{"application/json":{"schema":{"description":"Aggiornamento tags","type":"object","additionalProperties":true,"example":{"id":1,"programId":1,"name":"VIP","description":null,"tagType":"customers","color":"green","iconPath":null,"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"delete":{"tags":["Tags"],"description":"Elimina una programma fedeltà del programma indicato in `programId` (soft delete o `TERMINATED` se vincoli FK).\n\n**Contesto:** I **Tag** segmentano clienti e/o negozi (es. VIP, Promo Estate). Si usano in filtri promo, comunicazioni e reportistica.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n- `tagId` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **204**: Operazione riuscita senza corpo di risposta.\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Tags.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"tagId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"204":{"description":"Operazione riuscita senza corpo di risposta."},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/tags/{tagId}/customers/count":{"get":{"tags":["Tags"],"description":"Elenco paginato di count del programma indicato in `programId`.\n\n**Contesto:** I **Tag** segmentano clienti e/o negozi (es. VIP, Promo Estate). Si usano in filtri promo, comunicazioni e reportistica.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n- `tagId` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Conteggio clienti con tag.\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Tags.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"tagId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Conteggio clienti con tag.","content":{"application/json":{"schema":{"description":"Conteggio clienti con tag.","type":"object","additionalProperties":true,"example":{"count":42}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/tags/{id}/getCustomers":{"get":{"tags":["Tags"],"description":"Elenco paginato di getCustomers del programma indicato in `programId`.\n\n**Contesto:** I **Tag** segmentano clienti e/o negozi (es. VIP, Promo Estate). Si usano in filtri promo, comunicazioni e reportistica.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n- `id` (integer) — obbligatorio: Tag id\n\n**Querystring:**\n- `page` (integer) — opzionale [default: 1]\n- `size` (integer) — opzionale [default: 20]\n\n**Risposte di successo:**\n- **200**: Clienti con tag\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Tags.","parameters":[{"schema":{"type":"integer","default":1},"in":"query","name":"page","required":false},{"schema":{"type":"integer","default":20},"in":"query","name":"size","required":false},{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"id","required":true,"description":"Tag id"}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Clienti con tag","content":{"application/json":{"schema":{"description":"Clienti con tag","type":"object","additionalProperties":true,"example":{"content":[{"id":2,"firstName":"Mario","lastName":"Rossi","email":"m@example.com","customerToken":"K7Q2M9"}],"totalElements":1,"totalPages":1,"page":1,"size":20}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/tags/{id}/setTagsCustomers":{"post":{"tags":["Tags"],"description":"Assign this tag to the given list of customers. Each customer must belong to the same program.\n\n**Contesto:** I **Tag** segmentano clienti e/o negozi (es. VIP, Promo Estate). Si usano in filtri promo, comunicazioni e reportistica.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n- `id` (integer) — obbligatorio: Tag id\n\n**Body JSON:**\n- `customerIds` (array) — obbligatorio: List of customer ids to assign the tag to\n\n**Risposte di successo:**\n- **200**: Assegna tag a clienti.\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Tags.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["customerIds"],"properties":{"customerIds":{"type":"array","items":{"type":"integer"},"description":"List of customer ids to assign the tag to"}}}}},"required":true},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"id","required":true,"description":"Tag id"}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Assegna tag a clienti.","content":{"application/json":{"schema":{"description":"Assegna tag a clienti.","type":"object","additionalProperties":true,"example":{"success":true}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/tags/{tagId}/shops/count":{"get":{"tags":["Tags"],"description":"Elenco paginato di count del programma indicato in `programId`.\n\n**Contesto:** I **Tag** segmentano clienti e/o negozi (es. VIP, Promo Estate). Si usano in filtri promo, comunicazioni e reportistica.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n- `tagId` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Conteggio negozi con tag.\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Tags.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"tagId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Conteggio negozi con tag.","content":{"application/json":{"schema":{"description":"Conteggio negozi con tag.","type":"object","additionalProperties":true,"example":{"count":12}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/tags/{id}/getShops":{"get":{"tags":["Tags"],"description":"Elenco paginato di getShops del programma indicato in `programId`.\n\n**Contesto:** I **Tag** segmentano clienti e/o negozi (es. VIP, Promo Estate). Si usano in filtri promo, comunicazioni e reportistica.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n- `id` (integer) — obbligatorio: Tag id\n\n**Querystring:**\n- `page` (integer) — opzionale [default: 1]\n- `size` (integer) — opzionale [default: 20]\n\n**Risposte di successo:**\n- **200**: Negozi con tag\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Tags.","parameters":[{"schema":{"type":"integer","default":1},"in":"query","name":"page","required":false},{"schema":{"type":"integer","default":20},"in":"query","name":"size","required":false},{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"id","required":true,"description":"Tag id"}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Negozi con tag","content":{"application/json":{"schema":{"description":"Negozi con tag","type":"object","additionalProperties":true,"example":{"content":[{"id":1,"name":"Negozio centro","description":null,"companyName":null,"programId":1,"netId":1,"addressId":1,"address":{"id":1,"street":"Via Roma","number":"1","floor":null,"apartment":null,"zipCode":"50100","city":"Firenze","state":"FI","country":"IT","latitude":null,"longitude":null},"phone":"+39055123456","email":null,"foreignId":null,"taxNumber":null,"localCurrencyId":1,"timezoneId":2,"status":"ENABLED","website":"https://negozio.example","ecommerce":"https://shop.example","logo":"https://cdn.example/logo.png","contactPerson":"Mario Rossi","openingHours":[{"day":"MONDAY","ranges":[{"start":"09:00","end":"13:00"},{"start":"15:00","end":"19:00"}]},{"day":"SATURDAY","ranges":[{"start":"09:00","end":"12:30"}]}],"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z","lastTransactionAt":"2026-01-15T10:00:00.000Z","lastTerminalUseAt":"2026-01-15T10:00:00.000Z","newShop":false}],"totalElements":1,"totalPages":1,"page":1,"size":20}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/tags/{id}/setTagsShops":{"post":{"tags":["Tags"],"description":"Assign this tag to the given list of shops. Each shop must belong to the same program.\n\n**Contesto:** I **Tag** segmentano clienti e/o negozi (es. VIP, Promo Estate). Si usano in filtri promo, comunicazioni e reportistica.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n- `id` (integer) — obbligatorio: Tag id\n\n**Body JSON:**\n- `shopIds` (array) — obbligatorio: List of shop ids to assign the tag to\n\n**Risposte di successo:**\n- **200**: Assegna tag a negozi.\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Tags.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["shopIds"],"properties":{"shopIds":{"type":"array","items":{"type":"integer"},"description":"List of shop ids to assign the tag to"}}}}},"required":true},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"id","required":true,"description":"Tag id"}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Assegna tag a negozi.","content":{"application/json":{"schema":{"description":"Assegna tag a negozi.","type":"object","additionalProperties":true,"example":{"success":true}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/webhooks":{"get":{"tags":["Webhooks"],"description":"Elenco paginato di webhook del programma indicato in `programId`.\n\n**Contesto:** I **Webhook** notificano sistemi esterni su eventi (movimento, promo, cambio tier). Configurabili con URL, trigger e payload template.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n\n**Querystring:**\n- `page` (integer) — opzionale [default: 1]\n- `size` (integer) — opzionale [default: 20]\n\n**Risposte di successo:**\n- **200**: Lista webhooks\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Webhooks.","parameters":[{"schema":{"type":"integer","minimum":1,"default":1},"in":"query","name":"page","required":false},{"schema":{"type":"integer","minimum":1,"maximum":500,"default":20},"in":"query","name":"size","required":false},{"schema":{"type":"integer"},"in":"path","name":"programId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Lista webhooks","content":{"application/json":{"schema":{"description":"Lista webhooks","type":"object","additionalProperties":true,"example":{"content":[{"id":1,"programId":1,"url":"https://example.com/hook","eventTypes":["SALE"],"enabled":true,"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}],"totalElements":1,"totalPages":1,"page":1,"size":20}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"post":{"tags":["Webhooks"],"description":"Crea webhook del programma indicato in `programId`.\n\n**Contesto:** I **Webhook** notificano sistemi esterni su eventi (movimento, promo, cambio tier). Configurabili con URL, trigger e payload template.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n\n**Body JSON:**\n- `name` (string) — obbligatorio\n- `description` (string) — opzionale\n- `endpointUrl` (string) — obbligatorio\n- `httpMethod` (string) — opzionale (valori: GET, POST, PUT, PATCH, DELETE) [default: \"POST\"]\n- `headers` (object) — opzionale\n- `enabled` (boolean) — opzionale [default: true]\n- `timeoutSeconds` (integer) — opzionale [default: 30]\n- `retryCount` (integer) — opzionale [default: 3]\n- `retryDelaySeconds` (integer) — opzionale [default: 5]\n- `triggers` (array) — opzionale\n\n**Risposte di successo:**\n- **201**: Risorsa creata (webhooks)\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Webhooks.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["name","endpointUrl"],"properties":{"name":{"type":"string"},"description":{"type":"string"},"endpointUrl":{"type":"string","format":"uri"},"httpMethod":{"type":"string","enum":["GET","POST","PUT","PATCH","DELETE"],"default":"POST"},"headers":{"type":"object"},"enabled":{"type":"boolean","default":true},"timeoutSeconds":{"type":"integer","default":30},"retryCount":{"type":"integer","default":3},"retryDelaySeconds":{"type":"integer","default":5},"triggers":{"type":"array","items":{"type":"object","properties":{"triggerType":{"type":"string"},"triggerEvent":{"type":"string","nullable":true},"conditions":{"type":"object"}}}}}}}},"required":true},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"201":{"description":"Risorsa creata (webhooks)","content":{"application/json":{"schema":{"description":"Risorsa creata (webhooks)","type":"object","additionalProperties":true,"example":{"id":1,"programId":1,"url":"https://example.com/hook","eventTypes":["SALE"],"enabled":true,"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/webhooks/{webhookId}":{"get":{"tags":["Webhooks"],"description":"Dettaglio di una programma fedeltà per identificativo del programma indicato in `programId`.\n\n**Contesto:** I **Webhook** notificano sistemi esterni su eventi (movimento, promo, cambio tier). Configurabili con URL, trigger e payload template.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n- `webhookId` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Dettaglio webhooks\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Webhooks.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"webhookId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Dettaglio webhooks","content":{"application/json":{"schema":{"description":"Dettaglio webhooks","type":"object","additionalProperties":true,"example":{"id":1,"programId":1,"url":"https://example.com/hook","eventTypes":["SALE"],"enabled":true,"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"put":{"tags":["Webhooks"],"description":"Aggiorna una programma fedeltà (campi consentiti) del programma indicato in `programId`.\n\n**Contesto:** I **Webhook** notificano sistemi esterni su eventi (movimento, promo, cambio tier). Configurabili con URL, trigger e payload template.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n- `webhookId` (integer) — obbligatorio\n\n**Body JSON:**\n- `name` (string) — opzionale\n- `description` (string) — opzionale\n- `endpointUrl` (string) — opzionale\n- `httpMethod` (string) — opzionale (valori: GET, POST, PUT, PATCH, DELETE)\n- `headers` (object) — opzionale\n- `enabled` (boolean) — opzionale\n- `timeoutSeconds` (integer) — opzionale\n- `retryCount` (integer) — opzionale\n- `retryDelaySeconds` (integer) — opzionale\n- `triggers` (array) — opzionale\n\n**Risposte di successo:**\n- **200**: Aggiornamento webhooks\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Webhooks.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"description":{"type":"string"},"endpointUrl":{"type":"string","format":"uri"},"httpMethod":{"type":"string","enum":["GET","POST","PUT","PATCH","DELETE"]},"headers":{"type":"object"},"enabled":{"type":"boolean"},"timeoutSeconds":{"type":"integer"},"retryCount":{"type":"integer"},"retryDelaySeconds":{"type":"integer"},"triggers":{"type":"array","items":{"type":"object","properties":{"triggerType":{"type":"string"},"triggerEvent":{"type":"string","nullable":true},"conditions":{"type":"object"}}}}}}}}},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"webhookId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Aggiornamento webhooks","content":{"application/json":{"schema":{"description":"Aggiornamento webhooks","type":"object","additionalProperties":true,"example":{"id":1,"programId":1,"url":"https://example.com/hook","eventTypes":["SALE"],"enabled":true,"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"delete":{"tags":["Webhooks"],"description":"Elimina una programma fedeltà del programma indicato in `programId` (soft delete o `TERMINATED` se vincoli FK).\n\n**Contesto:** I **Webhook** notificano sistemi esterni su eventi (movimento, promo, cambio tier). Configurabili con URL, trigger e payload template.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n- `webhookId` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **204**: Operazione riuscita senza corpo di risposta.\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Webhooks.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"webhookId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"204":{"description":"Operazione riuscita senza corpo di risposta."},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/webhooks/whatsapp-meta":{"get":{"tags":["Webhooks"],"description":"Verifica webhook Meta WhatsApp per programma (hub.mode / hub.verify_token / hub.challenge). Pubblico.","parameters":[{"schema":{"type":"string"},"in":"query","name":"hub.mode","required":false},{"schema":{"type":"string"},"in":"query","name":"hub.verify_token","required":false},{"schema":{"type":"string"},"in":"query","name":"hub.challenge","required":false},{"schema":{"type":"integer"},"in":"path","name":"programId","required":true}],"responses":{"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"post":{"tags":["Webhooks"],"description":"Webhook inbound Meta WhatsApp scoped al programma nel path. Pubblico.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true}],"responses":{"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/webhooks/whatsapp-meta":{"get":{"tags":["Webhooks"],"description":"DEPRECATED. Preferire /api/v1/programs/:programId/webhooks/whatsapp-meta.","parameters":[{"schema":{"type":"string"},"in":"query","name":"hub.mode","required":false},{"schema":{"type":"string"},"in":"query","name":"hub.verify_token","required":false},{"schema":{"type":"string"},"in":"query","name":"hub.challenge","required":false}],"deprecated":true,"responses":{"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"post":{"tags":["Webhooks"],"description":"DEPRECATED. Preferire /api/v1/programs/:programId/webhooks/whatsapp-meta.","deprecated":true,"responses":{"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/audiences":{"get":{"tags":["Audiences"],"description":"Elenco paginato di audience del programma indicato in `programId`.\n\n**Contesto:** Le **Audience** sono segmenti dinamici di clienti (filtri salvati). Si usano in promo, comunicazioni e campagne voucher per targetizzare gruppi specifici.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n\n**Querystring:**\n- `page` (integer) — opzionale [default: 1]\n- `size` (integer) — opzionale [default: 20]\n\n**Risposte di successo:**\n- **200**: Lista audiences\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Audiences.","parameters":[{"schema":{"type":"integer","minimum":1,"default":1},"in":"query","name":"page","required":false},{"schema":{"type":"integer","minimum":1,"maximum":500,"default":20},"in":"query","name":"size","required":false},{"schema":{"type":"integer"},"in":"path","name":"programId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Lista audiences","content":{"application/json":{"schema":{"description":"Lista audiences","type":"object","additionalProperties":true,"example":{"content":[{"id":1,"programId":1,"name":"Clienti attivi","description":null,"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}],"totalElements":1,"totalPages":1,"page":1,"size":20}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"post":{"tags":["Audiences"],"description":"Crea audience del programma indicato in `programId`.\n\n**Contesto:** Le **Audience** sono segmenti dinamici di clienti (filtri salvati). Si usano in promo, comunicazioni e campagne voucher per targetizzare gruppi specifici.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n\n**Body JSON:**\n- `name` (string) — obbligatorio\n- `description` (string) — opzionale\n- `audienceType` (string) — obbligatorio (valori: STATIC, DYNAMIC)\n- `queryConfig` (object) — obbligatorio\n\n**Risposte di successo:**\n- **201**: Risorsa creata (audiences)\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Audiences.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["name","audienceType","queryConfig"],"properties":{"name":{"type":"string"},"description":{"type":"string"},"audienceType":{"type":"string","enum":["STATIC","DYNAMIC"]},"queryConfig":{"type":"object"}}}}},"required":true},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"201":{"description":"Risorsa creata (audiences)","content":{"application/json":{"schema":{"description":"Risorsa creata (audiences)","type":"object","additionalProperties":true,"example":{"id":1,"programId":1,"name":"Clienti attivi","description":null,"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/audiences/{audienceId}":{"get":{"tags":["Audiences"],"description":"Dettaglio di una programma fedeltà per identificativo del programma indicato in `programId`.\n\n**Contesto:** Le **Audience** sono segmenti dinamici di clienti (filtri salvati). Si usano in promo, comunicazioni e campagne voucher per targetizzare gruppi specifici.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n- `audienceId` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Dettaglio audiences\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Audiences.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"audienceId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Dettaglio audiences","content":{"application/json":{"schema":{"description":"Dettaglio audiences","type":"object","additionalProperties":true,"example":{"id":1,"programId":1,"name":"Clienti attivi","description":null,"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"put":{"tags":["Audiences"],"description":"Aggiorna una programma fedeltà (campi consentiti) del programma indicato in `programId`.\n\n**Contesto:** Le **Audience** sono segmenti dinamici di clienti (filtri salvati). Si usano in promo, comunicazioni e campagne voucher per targetizzare gruppi specifici.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n- `audienceId` (integer) — obbligatorio\n\n**Body JSON:**\n- `name` (string) — opzionale\n- `description` (string) — opzionale\n- `queryConfig` (object) — opzionale\n\n**Risposte di successo:**\n- **200**: Aggiornamento audiences\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Audiences.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"description":{"type":"string"},"queryConfig":{"type":"object"}}}}}},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"audienceId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Aggiornamento audiences","content":{"application/json":{"schema":{"description":"Aggiornamento audiences","type":"object","additionalProperties":true,"example":{"id":1,"programId":1,"name":"Clienti attivi","description":null,"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"delete":{"tags":["Audiences"],"description":"Elimina una programma fedeltà del programma indicato in `programId` (soft delete o `TERMINATED` se vincoli FK).\n\n**Contesto:** Le **Audience** sono segmenti dinamici di clienti (filtri salvati). Si usano in promo, comunicazioni e campagne voucher per targetizzare gruppi specifici.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n- `audienceId` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **204**: Operazione riuscita senza corpo di risposta.\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Audiences.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"audienceId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"204":{"description":"Operazione riuscita senza corpo di risposta."},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/audiences/{audienceId}/customers":{"get":{"tags":["Audiences"],"description":"Elenco paginato di cliente del programma indicato in `programId`.\n\n**Contesto:** Le **Audience** sono segmenti dinamici di clienti (filtri salvati). Si usano in promo, comunicazioni e campagne voucher per targetizzare gruppi specifici.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n- `audienceId` (integer) — obbligatorio\n\n**Querystring:**\n- `page` (integer) — opzionale [default: 1]\n- `size` (integer) — opzionale [default: 20]\n- `onlyIds` (boolean) — opzionale [default: false]\n- `onlyCard` (boolean) — opzionale [default: false]\n- `onlyPhone` (boolean) — opzionale [default: false]\n- `onlyEmail` (boolean) — opzionale [default: false]\n- `onlyFiscalCode` (boolean) — opzionale [default: false]\n\n**Risposte di successo:**\n- **200**: Clienti in audience\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Audiences.","parameters":[{"schema":{"type":"integer","minimum":1,"default":1},"in":"query","name":"page","required":false},{"schema":{"type":"integer","minimum":1,"maximum":500,"default":20},"in":"query","name":"size","required":false},{"schema":{"type":"boolean","default":false},"in":"query","name":"onlyIds","required":false},{"schema":{"type":"boolean","default":false},"in":"query","name":"onlyCard","required":false},{"schema":{"type":"boolean","default":false},"in":"query","name":"onlyPhone","required":false},{"schema":{"type":"boolean","default":false},"in":"query","name":"onlyEmail","required":false},{"schema":{"type":"boolean","default":false},"in":"query","name":"onlyFiscalCode","required":false},{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"audienceId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Clienti in audience","content":{"application/json":{"schema":{"description":"Clienti in audience","type":"object","additionalProperties":true,"example":{"content":[{"id":2,"firstName":"Luigi","lastName":"Verdi","customerToken":"Z9Y8X7"}],"totalElements":1,"totalPages":1,"page":1,"size":20}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/audiences/{audienceId}/calculate":{"post":{"tags":["Audiences"],"description":"Ricalcola una audience DYNAMIC: valuta i filtri e salva i clienti in audience_customers. Il conteggio e l'elenco paginato leggono quel risultato.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"audienceId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Audience ricalcolata","content":{"application/json":{"schema":{"description":"Audience ricalcolata","type":"object","additionalProperties":true,"example":{"id":67,"customerCount":1,"lastCalculatedAt":"2026-10-09T14:30:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/prizes/suggested":{"get":{"summary":"Suggested prizes (wishlist, co-views, trending)","tags":["PrizeEngagement"],"description":"Elenco paginato di suggested del programma indicato in `programId`.\n\n**Contesto:** L'**Engagement premi** registra visualizzazioni, interazioni e suggerimenti personalizzati per aumentare conversione al riscatto.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n\n**Querystring:**\n- `customerId` (integer) — obbligatorio\n- `limit` (integer) — opzionale [default: 10]\n\n**Risposte di successo:**\n- **200**: Premi suggeriti (wishlist, co-view, trending).\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** PrizeEngagement.","parameters":[{"schema":{"type":"integer"},"in":"query","name":"customerId","required":true},{"schema":{"type":"integer","minimum":1,"maximum":50,"default":10},"in":"query","name":"limit","required":false},{"schema":{"type":"integer"},"in":"path","name":"programId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Premi suggeriti (wishlist, co-view, trending).","content":{"application/json":{"schema":{"description":"Premi suggeriti (wishlist, co-view, trending).","type":"object","additionalProperties":true,"example":{"items":[{"id":50,"name":"Maglietta","description":null,"programId":1,"catalogId":10,"enabled":true,"status":"ENABLED","imageUrl":"https://cdn.example/prize.jpg","images":[{"id":1,"url":"https://cdn.example/prize.jpg","sortOrder":0}],"suggestionSource":"wishlist","createdAt":"2026-05-15T10:00:00.000Z","updatedAt":"2026-05-15T10:00:00.000Z"}]}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/prizes-catalogs":{"get":{"tags":["PrizesCatalogs"],"description":"Elenco paginato di catalogo premi del programma indicato in `programId`.\n\n**Contesto:** I **Cataloghi premi** raggruppano i premi disponibili al riscatto. Possono richiedere conferma backoffice e regolare visibilità per tier o audience.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n\n**Querystring:**\n- `page` (integer) — opzionale [default: 1]\n- `size` (integer) — opzionale [default: 20]\n- `search` (string) — opzionale\n\n**Risposte di successo:**\n- **200**: Lista prizes-catalogs\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** PrizesCatalogs.","parameters":[{"schema":{"type":"integer","default":1},"in":"query","name":"page","required":false},{"schema":{"type":"integer","default":20},"in":"query","name":"size","required":false},{"schema":{"type":"string"},"in":"query","name":"search","required":false},{"schema":{"type":"integer"},"in":"path","name":"programId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Lista prizes-catalogs","content":{"application/json":{"schema":{"description":"Lista prizes-catalogs","type":"object","additionalProperties":true,"example":{"content":[{"id":1,"programId":1,"name":"Catalogo premi","description":null,"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}],"totalElements":1,"totalPages":1,"page":1,"size":20}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"post":{"tags":["PrizesCatalogs"],"description":"Crea catalogo premi del programma indicato in `programId`.\n\n**Contesto:** I **Cataloghi premi** raggruppano i premi disponibili al riscatto. Possono richiedere conferma backoffice e regolare visibilità per tier o audience.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n\n**Body JSON:**\n- `name` (string) — obbligatorio\n- `description` (string) — opzionale\n- `descriptionLong` (string) — opzionale\n- `prizeCategoryId` (integer) — opzionale\n- `validFrom` (string) — opzionale\n- `validTo` (string) — opzionale\n- `posVisible` (boolean) — opzionale\n- `requestShopsMode` (string) — opzionale (valori: ALL, LIST, )\n- `pickupShopsMode` (string) — opzionale (valori: ALL, LIST) [default: \"ALL\"]\n- `homeDeliveryEnabled` (boolean) — opzionale\n- `homeDeliveryExclusive` (boolean) — opzionale\n- `customerVisibilityMode` (string) — opzionale (valori: ALL, CLUSTER) [default: \"ALL\"]\n- `enabled` (boolean) — opzionale\n- `requiresConfirmation` (boolean) — opzionale\n- `sendToExternalSupplier` (boolean) — opzionale\n- `externalSupplierId` (integer) — opzionale\n- `allowPickupShopChangeBO` (boolean) — opzionale\n- `status` (string) — opzionale (valori: ENABLED, DISABLED)\n\n**Risposte di successo:**\n- **201**: Risorsa creata (prizes-catalogs)\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** PrizesCatalogs.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["name"],"additionalProperties":true,"properties":{"name":{"type":"string"},"description":{"type":"string"},"descriptionLong":{"type":"string"},"prizeCategoryId":{"type":"integer","nullable":true},"validFrom":{"type":"string","format":"date","nullable":true},"validTo":{"type":"string","format":"date","nullable":true},"posVisible":{"type":"boolean"},"requestShopsMode":{"type":"string","enum":["ALL","LIST",null],"nullable":true},"pickupShopsMode":{"type":"string","enum":["ALL","LIST"],"default":"ALL"},"homeDeliveryEnabled":{"type":"boolean"},"homeDeliveryExclusive":{"type":"boolean"},"customerVisibilityMode":{"type":"string","enum":["ALL","CLUSTER"],"default":"ALL"},"enabled":{"type":"boolean"},"requiresConfirmation":{"type":"boolean"},"sendToExternalSupplier":{"type":"boolean"},"externalSupplierId":{"type":"integer","nullable":true},"allowPickupShopChangeBO":{"type":"boolean"},"status":{"type":"string","enum":["ENABLED","DISABLED"]}}}}},"required":true},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"201":{"description":"Risorsa creata (prizes-catalogs)","content":{"application/json":{"schema":{"description":"Risorsa creata (prizes-catalogs)","type":"object","additionalProperties":true,"example":{"id":1,"programId":1,"name":"Catalogo premi","description":null,"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/prizes-categories":{"get":{"tags":["PrizesCategories"],"description":"Elenco paginato di categoria premio del programma indicato in `programId`.\n\n**Contesto:** Le **Categorie premio** classificano i premi all'interno di un catalogo (gadget, esperienze, sconti, ecc.).\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n\n**Querystring:**\n- `page` (integer) — opzionale [default: 1]\n- `size` (integer) — opzionale [default: 20]\n\n**Risposte di successo:**\n- **200**: Lista prizes-categories\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** PrizesCategories.","parameters":[{"schema":{"type":"integer","minimum":1,"default":1},"in":"query","name":"page","required":false},{"schema":{"type":"integer","minimum":1,"maximum":500,"default":20},"in":"query","name":"size","required":false},{"schema":{"type":"integer"},"in":"path","name":"programId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Lista prizes-categories","content":{"application/json":{"schema":{"description":"Lista prizes-categories","type":"object","additionalProperties":true,"example":{"content":[{"id":1,"programId":1,"name":"Gadget","description":null,"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}],"totalElements":1,"totalPages":1,"page":1,"size":20}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"post":{"tags":["PrizesCategories"],"description":"Crea categoria premio del programma indicato in `programId`.\n\n**Contesto:** Le **Categorie premio** classificano i premi all'interno di un catalogo (gadget, esperienze, sconti, ecc.).\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n\n**Body JSON:**\n- `name` (string) — obbligatorio\n- `description` (string) — opzionale\n\n**Risposte di successo:**\n- **201**: Risorsa creata (prizes-categories)\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** PrizesCategories.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["name"],"properties":{"name":{"type":"string"},"description":{"type":"string"}}}}},"required":true},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"201":{"description":"Risorsa creata (prizes-categories)","content":{"application/json":{"schema":{"description":"Risorsa creata (prizes-categories)","type":"object","additionalProperties":true,"example":{"id":1,"programId":1,"name":"Gadget","description":null,"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/prizes":{"get":{"tags":["Prizes"],"description":"Elenco paginato di premio del programma indicato in `programId`.\n\n**Contesto:** I **Premi** sono ricompense riscattabili con wallet/punti o pagamento misto. Hanno tipo (fisico, digitale), stock, opzioni di pagamento e fulfillment (ritiro, spedizione).\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n\n**Querystring:**\n- `categoryId` (integer) — opzionale\n- `catalogId` (integer) — opzionale\n- `search` (string) — opzionale: Filtro nome premio (ILIKE)\n- `customerId` (integer) — opzionale: When set, filter by catalog visibility for this customer\n- `page` (integer) — opzionale [default: 1]\n- `size` (integer) — opzionale [default: 20]\n\n**Risposte di successo:**\n- **200**: Lista prizes\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Prizes.","parameters":[{"schema":{"type":"integer"},"in":"query","name":"categoryId","required":false},{"schema":{"type":"integer"},"in":"query","name":"catalogId","required":false},{"schema":{"type":"string"},"in":"query","name":"search","required":false,"description":"Filtro nome premio (ILIKE)"},{"schema":{"type":"integer"},"in":"query","name":"customerId","required":false,"description":"When set, filter by catalog visibility for this customer"},{"schema":{"type":"integer","default":1},"in":"query","name":"page","required":false},{"schema":{"type":"integer","default":20},"in":"query","name":"size","required":false},{"schema":{"type":"integer"},"in":"path","name":"programId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Lista prizes","content":{"application/json":{"schema":{"description":"Lista prizes","type":"object","additionalProperties":true,"example":{"content":[{"id":1,"programId":1,"name":"Premio","description":null,"prizeType":"PHYSICAL","status":"ENABLED","createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}],"totalElements":1,"totalPages":1,"page":1,"size":20}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"post":{"tags":["Prizes"],"description":"Crea premio del programma indicato in `programId`.\n\n**Contesto:** I **Premi** sono ricompense riscattabili con wallet/punti o pagamento misto. Hanno tipo (fisico, digitale), stock, opzioni di pagamento e fulfillment (ritiro, spedizione).\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n\n**Body JSON:**\n- `name` (string) — obbligatorio\n- `description` (string) — opzionale\n- `descriptionLong` (string) — opzionale\n- `categoryId` (integer) — opzionale\n- `subcategoryId` (integer) — opzionale\n- `catalogId` (integer) — opzionale\n- `foreignId` (string) — opzionale\n- `brand` (string) — opzionale\n- `maxQuantityPerRequest` (integer) — opzionale\n- `enabled` (boolean) — opzionale\n- `requestShopsMode` (string) — opzionale (valori: ADDITIVE, EXCLUSIVE, )\n- `pickupShopsMode` (string) — opzionale (valori: ADDITIVE, EXCLUSIVE, )\n- `homeDeliveryEnabled` (boolean) — opzionale\n- `status` (string) — opzionale (valori: ENABLED, DISABLED)\n- `imageUrl` (string) — opzionale\n- `images` (array) — opzionale\n\n**Risposte di successo:**\n- **201**: Risorsa creata (prizes)\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Prizes.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["name"],"additionalProperties":true,"properties":{"name":{"type":"string"},"description":{"type":"string"},"descriptionLong":{"type":"string"},"categoryId":{"type":"integer","nullable":true},"subcategoryId":{"type":"integer","nullable":true},"catalogId":{"type":"integer","nullable":true},"foreignId":{"type":"string","maxLength":100},"brand":{"type":"string","maxLength":255},"maxQuantityPerRequest":{"type":"integer","minimum":1,"nullable":true},"enabled":{"type":"boolean"},"requestShopsMode":{"type":"string","enum":["ADDITIVE","EXCLUSIVE",null],"nullable":true},"pickupShopsMode":{"type":"string","enum":["ADDITIVE","EXCLUSIVE",null],"nullable":true},"homeDeliveryEnabled":{"type":"boolean"},"status":{"type":"string","enum":["ENABLED","DISABLED"]},"imageUrl":{"type":"string","nullable":true},"duplicateImages":{"type":"boolean","description":"true: copia l'immagine su S3 di questo programma anche se l'URL è già nel bucket (altro programma). false: salva l'URL così com'è. Omesso: gli URL esterni vengono copiati, quelli già nel bucket riusati."},"images":{"type":"array","items":{"type":"object","properties":{"url":{"type":"string"},"sortOrder":{"type":"integer"}}}}}}}},"required":true},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"201":{"description":"Risorsa creata (prizes)","content":{"application/json":{"schema":{"description":"Risorsa creata (prizes)","type":"object","additionalProperties":true,"example":{"id":1,"programId":1,"name":"Premio","description":null,"prizeType":"PHYSICAL","status":"ENABLED","createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/prizes/{id}/exchange":{"post":{"tags":["Prizes"],"description":"Riscatta un premio per un cliente (crea richiesta premio / movimento).\n\n**Contesto:** I **Premi** sono ricompense riscattabili con wallet/punti o pagamento misto. Hanno tipo (fisico, digitale), stock, opzioni di pagamento e fulfillment (ritiro, spedizione).\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `id` (integer) — obbligatorio\n\n**Body JSON:**\n- `customerId` (integer) — obbligatorio\n- `quantity` (integer) — opzionale [default: 1]\n- `shippingAddress` (object) — opzionale\n\n**Risposte di successo:**\n- **201**: Riscatto premio\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Prizes.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["customerId"],"properties":{"customerId":{"type":"integer"},"quantity":{"type":"integer","default":1},"shippingAddress":{"type":"object","properties":{"street":{"type":"string"},"number":{"type":"string"},"city":{"type":"string"},"zipCode":{"type":"string"},"country":{"type":"string"}}}}}}},"required":true},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"201":{"description":"Riscatto premio","content":{"application/json":{"schema":{"description":"Riscatto premio","type":"object","additionalProperties":true,"example":{"exchangeId":1,"prizeId":1,"customerId":2}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/prizes-catalogs/{id}":{"get":{"tags":["PrizesCatalogs"],"description":"Dettaglio di una programma fedeltà per identificativo del programma indicato in `programId`.\n\n**Contesto:** I **Cataloghi premi** raggruppano i premi disponibili al riscatto. Possono richiedere conferma backoffice e regolare visibilità per tier o audience.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n- `id` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Dettaglio prizes-catalogs\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** PrizesCatalogs.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Dettaglio prizes-catalogs","content":{"application/json":{"schema":{"description":"Dettaglio prizes-catalogs","type":"object","additionalProperties":true,"example":{"id":1,"programId":1,"name":"Catalogo premi","description":null,"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"put":{"tags":["PrizesCatalogs"],"description":"Aggiorna una programma fedeltà (campi consentiti) del programma indicato in `programId`.\n\n**Contesto:** I **Cataloghi premi** raggruppano i premi disponibili al riscatto. Possono richiedere conferma backoffice e regolare visibilità per tier o audience.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n- `id` (integer) — obbligatorio\n\n**Body JSON:**\n- `name` (string) — opzionale\n- `description` (string) — opzionale\n- `descriptionLong` (string) — opzionale\n- `prizeCategoryId` (integer) — opzionale\n- `validFrom` (string) — opzionale\n- `validTo` (string) — opzionale\n- `posVisible` (boolean) — opzionale\n- `requestShopsMode` (string) — opzionale (valori: ALL, LIST, )\n- `pickupShopsMode` (string) — opzionale (valori: ALL, LIST)\n- `homeDeliveryEnabled` (boolean) — opzionale\n- `homeDeliveryExclusive` (boolean) — opzionale\n- `customerVisibilityMode` (string) — opzionale (valori: ALL, CLUSTER)\n- `enabled` (boolean) — opzionale\n- `requiresConfirmation` (boolean) — opzionale\n- `sendToExternalSupplier` (boolean) — opzionale\n- `externalSupplierId` (integer) — opzionale\n- `allowPickupShopChangeBO` (boolean) — opzionale\n- `status` (string) — opzionale (valori: ENABLED, DISABLED)\n\n**Risposte di successo:**\n- **200**: Aggiornamento prizes-catalogs\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** PrizesCatalogs.","requestBody":{"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"properties":{"name":{"type":"string"},"description":{"type":"string"},"descriptionLong":{"type":"string"},"prizeCategoryId":{"type":"integer","nullable":true},"validFrom":{"type":"string","format":"date","nullable":true},"validTo":{"type":"string","format":"date","nullable":true},"posVisible":{"type":"boolean"},"requestShopsMode":{"type":"string","enum":["ALL","LIST",null],"nullable":true},"pickupShopsMode":{"type":"string","enum":["ALL","LIST"]},"homeDeliveryEnabled":{"type":"boolean"},"homeDeliveryExclusive":{"type":"boolean"},"customerVisibilityMode":{"type":"string","enum":["ALL","CLUSTER"]},"enabled":{"type":"boolean"},"requiresConfirmation":{"type":"boolean"},"sendToExternalSupplier":{"type":"boolean"},"externalSupplierId":{"type":"integer","nullable":true},"allowPickupShopChangeBO":{"type":"boolean"},"status":{"type":"string","enum":["ENABLED","DISABLED"]}}}}}},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Aggiornamento prizes-catalogs","content":{"application/json":{"schema":{"description":"Aggiornamento prizes-catalogs","type":"object","additionalProperties":true,"example":{"id":1,"programId":1,"name":"Catalogo premi","description":null,"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"delete":{"tags":["PrizesCatalogs"],"description":"Elimina una programma fedeltà del programma indicato in `programId` (soft delete o `TERMINATED` se vincoli FK).\n\n**Contesto:** I **Cataloghi premi** raggruppano i premi disponibili al riscatto. Possono richiedere conferma backoffice e regolare visibilità per tier o audience.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n- `id` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **204**: Operazione riuscita senza corpo di risposta.\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** PrizesCatalogs.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"204":{"description":"Operazione riuscita senza corpo di risposta."},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/prizes-categories/{id}":{"get":{"tags":["PrizesCategories"],"description":"Dettaglio di una programma fedeltà per identificativo del programma indicato in `programId`.\n\n**Contesto:** Le **Categorie premio** classificano i premi all'interno di un catalogo (gadget, esperienze, sconti, ecc.).\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n- `id` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Dettaglio prizes-categories\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** PrizesCategories.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Dettaglio prizes-categories","content":{"application/json":{"schema":{"description":"Dettaglio prizes-categories","type":"object","additionalProperties":true,"example":{"id":1,"programId":1,"name":"Gadget","description":null,"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"put":{"tags":["PrizesCategories"],"description":"Aggiorna una programma fedeltà (campi consentiti) del programma indicato in `programId`.\n\n**Contesto:** Le **Categorie premio** classificano i premi all'interno di un catalogo (gadget, esperienze, sconti, ecc.).\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n- `id` (integer) — obbligatorio\n\n**Body JSON:**\n- `name` (string) — opzionale\n- `description` (string) — opzionale\n\n**Risposte di successo:**\n- **200**: Aggiornamento prizes-categories\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** PrizesCategories.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"description":{"type":"string"}}}}}},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Aggiornamento prizes-categories","content":{"application/json":{"schema":{"description":"Aggiornamento prizes-categories","type":"object","additionalProperties":true,"example":{"id":1,"programId":1,"name":"Gadget","description":null,"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"delete":{"tags":["PrizesCategories"],"description":"Elimina una programma fedeltà del programma indicato in `programId` (soft delete o `TERMINATED` se vincoli FK).\n\n**Contesto:** Le **Categorie premio** classificano i premi all'interno di un catalogo (gadget, esperienze, sconti, ecc.).\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n- `id` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **204**: Operazione riuscita senza corpo di risposta.\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** PrizesCategories.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"204":{"description":"Operazione riuscita senza corpo di risposta."},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/prizes/{id}":{"get":{"tags":["Prizes"],"description":"Dettaglio di premio per identificativo.\n\n**Contesto:** I **Premi** sono ricompense riscattabili con wallet/punti o pagamento misto. Hanno tipo (fisico, digitale), stock, opzioni di pagamento e fulfillment (ritiro, spedizione).\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `id` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Premio (globale)\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Prizes.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Premio (globale)","content":{"application/json":{"schema":{"description":"Premio (globale)","type":"object","additionalProperties":true,"example":{"id":1,"programId":1,"name":"Premio","description":null,"prizeType":"PHYSICAL","status":"ENABLED","createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"put":{"tags":["Prizes"],"description":"Aggiorna premio (campi consentiti).\n\n**Contesto:** I **Premi** sono ricompense riscattabili con wallet/punti o pagamento misto. Hanno tipo (fisico, digitale), stock, opzioni di pagamento e fulfillment (ritiro, spedizione).\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `id` (integer) — obbligatorio\n\n**Body JSON:**\n- `name` (string) — opzionale\n- `description` (string) — opzionale\n- `descriptionLong` (string) — opzionale\n- `categoryId` (integer) — opzionale\n- `subcategoryId` (integer) — opzionale\n- `catalogId` (integer) — opzionale\n- `foreignId` (string) — opzionale\n- `brand` (string) — opzionale\n- `maxQuantityPerRequest` (integer) — opzionale\n- `enabled` (boolean) — opzionale\n- `requestShopsMode` (string) — opzionale (valori: ADDITIVE, EXCLUSIVE, )\n- `pickupShopsMode` (string) — opzionale (valori: ADDITIVE, EXCLUSIVE, )\n- `homeDeliveryEnabled` (boolean) — opzionale\n- `status` (string) — opzionale (valori: ENABLED, DISABLED)\n- `imageUrl` (string) — opzionale\n- `images` (array) — opzionale\n\n**Risposte di successo:**\n- **200**: Premio aggiornato\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Prizes.","requestBody":{"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"properties":{"name":{"type":"string"},"description":{"type":"string"},"descriptionLong":{"type":"string"},"categoryId":{"type":"integer","nullable":true},"subcategoryId":{"type":"integer","nullable":true},"catalogId":{"type":"integer","nullable":true},"foreignId":{"type":"string","maxLength":100},"brand":{"type":"string","maxLength":255},"maxQuantityPerRequest":{"type":"integer","minimum":1,"nullable":true},"enabled":{"type":"boolean"},"requestShopsMode":{"type":"string","enum":["ADDITIVE","EXCLUSIVE",null],"nullable":true},"pickupShopsMode":{"type":"string","enum":["ADDITIVE","EXCLUSIVE",null],"nullable":true},"homeDeliveryEnabled":{"type":"boolean"},"status":{"type":"string","enum":["ENABLED","DISABLED"]},"imageUrl":{"type":"string","nullable":true},"duplicateImages":{"type":"boolean"},"images":{"type":"array","items":{"type":"object","properties":{"url":{"type":"string"},"sortOrder":{"type":"integer"}}}}}}}}},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Premio aggiornato","content":{"application/json":{"schema":{"description":"Premio aggiornato","type":"object","additionalProperties":true,"example":{"id":1,"programId":1,"name":"Premio","description":null,"prizeType":"PHYSICAL","status":"ENABLED","createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"delete":{"tags":["Prizes"],"description":"Elimina premio (soft delete o `TERMINATED` se vincoli FK).\n\n**Contesto:** I **Premi** sono ricompense riscattabili con wallet/punti o pagamento misto. Hanno tipo (fisico, digitale), stock, opzioni di pagamento e fulfillment (ritiro, spedizione).\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `id` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **204**: Operazione riuscita senza corpo di risposta.\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Prizes.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"204":{"description":"Operazione riuscita senza corpo di risposta."},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/media/premi/upload":{"post":{"tags":["Media"],"description":"Carica un'immagine nel bucket S3 sotto `{programId}/Premi/`. Multipart: campo `file`.\n\n**Contesto:** Upload **media** (immagini/file) su storage S3 per negozi, premi, news e altre entità con URL pubblico.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Carica un'immagine nel bucket S3 sotto `{programId}/Premi/`. Multipart: campo file.\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Media.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true}],"responses":{"200":{"description":"Carica un'immagine nel bucket S3 sotto `{programId}/Premi/`. Multipart: campo file.","content":{"application/json":{"schema":{"description":"Carica un'immagine nel bucket S3 sotto `{programId}/Premi/`. Multipart: campo file.","type":"object","additionalProperties":true,"example":{"url":"https://bucket.s3.eu-west-1.amazonaws.com/1/Premi/a1b2c3d4.jpg","key":"1/Premi/a1b2c3d4.jpg"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/media/vouchers/upload":{"post":{"tags":["Media"],"description":"Carica un'immagine nel bucket S3 sotto `{programId}/Vouchers/`. Multipart: campo `file`.\n\n**Contesto:** Upload **media** (immagini/file) su storage S3 per negozi, premi, news e altre entità con URL pubblico.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Carica un'immagine nel bucket S3 sotto `{programId}/Vouchers/`. Multipart: campo file.\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Media.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true}],"responses":{"200":{"description":"Carica un'immagine nel bucket S3 sotto `{programId}/Vouchers/`. Multipart: campo file.","content":{"application/json":{"schema":{"description":"Carica un'immagine nel bucket S3 sotto `{programId}/Vouchers/`. Multipart: campo file.","type":"object","additionalProperties":true,"example":{"url":"https://bucket.s3.eu-west-1.amazonaws.com/1/Vouchers/a1b2c3d4.jpg","key":"1/Vouchers/a1b2c3d4.jpg"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/media/news/upload":{"post":{"tags":["Media"],"description":"Carica un'immagine nel bucket S3 sotto `{programId}/News/`. Multipart: campo `file`.\n\n**Contesto:** Upload **media** (immagini/file) su storage S3 per negozi, premi, news e altre entità con URL pubblico.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Carica un'immagine nel bucket S3 sotto `{programId}/News/`. Multipart: campo file.\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Media.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true}],"responses":{"200":{"description":"Carica un'immagine nel bucket S3 sotto `{programId}/News/`. Multipart: campo file.","content":{"application/json":{"schema":{"description":"Carica un'immagine nel bucket S3 sotto `{programId}/News/`. Multipart: campo file.","type":"object","additionalProperties":true,"example":{"url":"https://bucket.s3.eu-west-1.amazonaws.com/1/News/a1b2c3d4.jpg","key":"1/News/a1b2c3d4.jpg"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/media/promo/upload":{"post":{"tags":["Media"],"description":"Carica un'immagine nel bucket S3 sotto `{programId}/Promo/`. Multipart: campo `file`.\n\n**Contesto:** Upload **media** (immagini/file) su storage S3 per negozi, premi, news e altre entità con URL pubblico.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Carica un'immagine nel bucket S3 sotto `{programId}/Promo/`. Multipart: campo file.\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Media.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true}],"responses":{"200":{"description":"Carica un'immagine nel bucket S3 sotto `{programId}/Promo/`. Multipart: campo file.","content":{"application/json":{"schema":{"description":"Carica un'immagine nel bucket S3 sotto `{programId}/Promo/`. Multipart: campo file.","type":"object","additionalProperties":true,"example":{"url":"https://bucket.s3.eu-west-1.amazonaws.com/1/Promo/a1b2c3d4.jpg","key":"1/Promo/a1b2c3d4.jpg"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/media/donation-ongs/upload":{"post":{"tags":["Media"],"description":"Carica un'immagine nel bucket S3 sotto `{programId}/DonationOngs/`. Multipart: campo `file`.\n\n**Contesto:** Upload **media** (immagini/file) su storage S3 per negozi, premi, news e altre entità con URL pubblico.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Carica un'immagine nel bucket S3 sotto `{programId}/DonationOngs/`. Multipart: campo file.\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Media.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true}],"responses":{"200":{"description":"Carica un'immagine nel bucket S3 sotto `{programId}/DonationOngs/`. Multipart: campo file.","content":{"application/json":{"schema":{"description":"Carica un'immagine nel bucket S3 sotto `{programId}/DonationOngs/`. Multipart: campo file.","type":"object","additionalProperties":true,"example":{"url":"https://bucket.s3.eu-west-1.amazonaws.com/1/DonationOngs/a1b2c3d4.jpg","key":"1/DonationOngs/a1b2c3d4.jpg"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/media/shops/upload":{"post":{"tags":["Media"],"description":"Carica un'immagine nel bucket S3 sotto `{programId}/Shops/`. Multipart: campo `file`.\n\n**Contesto:** Upload **media** (immagini/file) su storage S3 per negozi, premi, news e altre entità con URL pubblico.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Carica un'immagine nel bucket S3 sotto `{programId}/Shops/`. Multipart: campo file.\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Media.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true}],"responses":{"200":{"description":"Carica un'immagine nel bucket S3 sotto `{programId}/Shops/`. Multipart: campo file.","content":{"application/json":{"schema":{"description":"Carica un'immagine nel bucket S3 sotto `{programId}/Shops/`. Multipart: campo file.","type":"object","additionalProperties":true,"example":{"url":"https://bucket.s3.eu-west-1.amazonaws.com/1/Shops/a1b2c3d4.jpg","key":"1/Shops/a1b2c3d4.jpg"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/media/coupon-offers/upload":{"post":{"tags":["Media"],"description":"Carica un'immagine nel bucket S3 sotto `{programId}/CouponOffers/`. Multipart: campo `file`.\n\n**Contesto:** Upload **media** (immagini/file) su storage S3 per negozi, premi, news e altre entità con URL pubblico.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Carica un'immagine nel bucket S3 sotto `{programId}/CouponOffers/`. Multipart: campo file.\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Media.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true}],"responses":{"200":{"description":"Carica un'immagine nel bucket S3 sotto `{programId}/CouponOffers/`. Multipart: campo file.","content":{"application/json":{"schema":{"description":"Carica un'immagine nel bucket S3 sotto `{programId}/CouponOffers/`. Multipart: campo file.","type":"object","additionalProperties":true,"example":{"url":"https://bucket.s3.eu-west-1.amazonaws.com/1/CouponOffers/a1b2c3d4.jpg","key":"1/CouponOffers/a1b2c3d4.jpg"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/media/gift-cards/upload":{"post":{"tags":["Media"],"description":"Carica un'immagine nel bucket S3 sotto `{programId}/GiftCards/`. Multipart: campo `file`.\n\n**Contesto:** Upload **media** (immagini/file) su storage S3 per negozi, premi, news e altre entità con URL pubblico.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Carica un'immagine nel bucket S3 sotto `{programId}/GiftCards/`. Multipart: campo file.\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Media.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true}],"responses":{"200":{"description":"Carica un'immagine nel bucket S3 sotto `{programId}/GiftCards/`. Multipart: campo file.","content":{"application/json":{"schema":{"description":"Carica un'immagine nel bucket S3 sotto `{programId}/GiftCards/`. Multipart: campo file.","type":"object","additionalProperties":true,"example":{"url":"https://bucket.s3.eu-west-1.amazonaws.com/1/GiftCards/a1b2c3d4.jpg","key":"1/GiftCards/a1b2c3d4.jpg"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/media/programs/upload":{"post":{"tags":["Media"],"description":"Carica un'immagine nel bucket S3 sotto `{programId}/Programs/`. Multipart: campo `file`.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true}],"responses":{"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/prize-suppliers":{"get":{"tags":["PrizeSuppliers"],"description":"Elenco paginato di prize-suppliers del programma indicato in `programId`.\n\n**Contesto:** Un **Programma** è l'istanza isolata del loyalty (ex Campaign): contiene negozi, clienti, wallet, promo, premi e tutte le configurazioni. Livello SYSTEM per la gestione piattaforma.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n\n**Querystring:**\n- `search` (string) — opzionale\n- `enabled` (boolean) — opzionale\n- `page` (integer) — opzionale [default: 1]\n- `size` (integer) — opzionale [default: 20]\n\n**Risposte di successo:**\n- **200**: Lista prize-suppliers\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** PrizeSuppliers.","parameters":[{"schema":{"type":"string"},"in":"query","name":"search","required":false},{"schema":{"type":"boolean"},"in":"query","name":"enabled","required":false},{"schema":{"type":"integer","default":1},"in":"query","name":"page","required":false},{"schema":{"type":"integer","default":20},"in":"query","name":"size","required":false},{"schema":{"type":"integer"},"in":"path","name":"programId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Lista prize-suppliers","content":{"application/json":{"schema":{"description":"Lista prize-suppliers","type":"object","additionalProperties":true,"example":{"content":[{"id":1,"name":"Esempio","programId":1,"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}],"totalElements":1,"totalPages":1,"page":1,"size":20}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"post":{"tags":["PrizeSuppliers"],"description":"Create prize supplier. Credentials are encrypted server-side (PRIZE_SUPPLIER_CRYPTO_KEY required).\n\n**Contesto:** Un **Programma** è l'istanza isolata del loyalty (ex Campaign): contiene negozi, clienti, wallet, promo, premi e tutte le configurazioni. Livello SYSTEM per la gestione piattaforma.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n\n**Body JSON:**\n- `name` (string) — obbligatorio\n- `description` (string) — opzionale\n- `apiBaseUrl` (string) — opzionale\n- `webhookUrl` (string) — opzionale\n- `authType` (string) — opzionale (valori: API_KEY, BEARER_TOKEN, BASIC, CUSTOM_HEADER, NONE) [default: \"API_KEY\"]\n- `credentials` (object) — opzionale\n- `extraConfig` (object) — opzionale\n- `enabled` (boolean) — opzionale [default: true]\n\n**Risposte di successo:**\n- **201**: Risorsa creata (prize-suppliers)\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** PrizeSuppliers.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["name"],"additionalProperties":true,"properties":{"name":{"type":"string"},"description":{"type":"string"},"apiBaseUrl":{"type":"string"},"webhookUrl":{"type":"string"},"authType":{"type":"string","enum":["API_KEY","BEARER_TOKEN","BASIC","CUSTOM_HEADER","NONE"],"default":"API_KEY"},"credentials":{"type":"object","additionalProperties":true},"extraConfig":{"type":"object","additionalProperties":true},"enabled":{"type":"boolean","default":true}}}}},"required":true},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"201":{"description":"Risorsa creata (prize-suppliers)","content":{"application/json":{"schema":{"description":"Risorsa creata (prize-suppliers)","type":"object","additionalProperties":true,"example":{"id":1,"name":"Esempio","programId":1,"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/prize-suppliers/{id}":{"get":{"tags":["PrizeSuppliers"],"description":"Dettaglio di una programma fedeltà per identificativo del programma indicato in `programId`.\n\n**Contesto:** Un **Programma** è l'istanza isolata del loyalty (ex Campaign): contiene negozi, clienti, wallet, promo, premi e tutte le configurazioni. Livello SYSTEM per la gestione piattaforma.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n- `id` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Dettaglio prize-suppliers\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** PrizeSuppliers.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Dettaglio prize-suppliers","content":{"application/json":{"schema":{"description":"Dettaglio prize-suppliers","type":"object","additionalProperties":true,"example":{"id":1,"name":"Esempio","programId":1,"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"put":{"tags":["PrizeSuppliers"],"description":"Update prize supplier. To rotate credentials, pass `credentials` again; to clear, pass an empty object.\n\n**Contesto:** Un **Programma** è l'istanza isolata del loyalty (ex Campaign): contiene negozi, clienti, wallet, promo, premi e tutte le configurazioni. Livello SYSTEM per la gestione piattaforma.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n- `id` (integer) — obbligatorio\n\n**Body JSON:**\n- `name` (string) — opzionale\n- `description` (string) — opzionale\n- `apiBaseUrl` (string) — opzionale\n- `webhookUrl` (string) — opzionale\n- `authType` (string) — opzionale (valori: API_KEY, BEARER_TOKEN, BASIC, CUSTOM_HEADER, NONE)\n- `credentials` (object) — opzionale\n- `clearCredentials` (boolean) — opzionale\n- `extraConfig` (object) — opzionale\n- `enabled` (boolean) — opzionale\n\n**Risposte di successo:**\n- **200**: Aggiornamento prize-suppliers\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** PrizeSuppliers.","requestBody":{"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"properties":{"name":{"type":"string"},"description":{"type":"string"},"apiBaseUrl":{"type":"string"},"webhookUrl":{"type":"string"},"authType":{"type":"string","enum":["API_KEY","BEARER_TOKEN","BASIC","CUSTOM_HEADER","NONE"]},"credentials":{"type":"object","additionalProperties":true},"clearCredentials":{"type":"boolean"},"extraConfig":{"type":"object","additionalProperties":true},"enabled":{"type":"boolean"}}}}}},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Aggiornamento prize-suppliers","content":{"application/json":{"schema":{"description":"Aggiornamento prize-suppliers","type":"object","additionalProperties":true,"example":{"id":1,"name":"Esempio","programId":1,"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"delete":{"tags":["PrizeSuppliers"],"description":"Soft-delete prize supplier. Fails if the supplier is referenced by enabled catalogs.\n\n**Contesto:** Un **Programma** è l'istanza isolata del loyalty (ex Campaign): contiene negozi, clienti, wallet, promo, premi e tutte le configurazioni. Livello SYSTEM per la gestione piattaforma.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n- `id` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **204**: Operazione riuscita senza corpo di risposta.\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** PrizeSuppliers.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"204":{"description":"Operazione riuscita senza corpo di risposta."},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/prizes-catalogs/{catalogId}/request-shops":{"get":{"tags":["PrizesCatalogs"],"description":"Elenco paginato di request-shops del programma indicato in `programId`.\n\n**Contesto:** I **Cataloghi premi** raggruppano i premi disponibili al riscatto. Possono richiedere conferma backoffice e regolare visibilità per tier o audience.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n- `catalogId` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Negozi request shops del catalogo premi.\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** PrizesCatalogs.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"catalogId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Negozi request shops del catalogo premi.","content":{"application/json":{"schema":{"description":"Negozi request shops del catalogo premi.","type":"object","additionalProperties":true,"example":{"content":[{"id":10,"shopId":42,"shopName":"Negozio centro"}]}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"put":{"tags":["PrizesCatalogs"],"description":"Aggiorna una programma fedeltà (campi consentiti) del programma indicato in `programId`.\n\n**Contesto:** I **Cataloghi premi** raggruppano i premi disponibili al riscatto. Possono richiedere conferma backoffice e regolare visibilità per tier o audience.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n- `catalogId` (integer) — obbligatorio\n\n**Body JSON:**\n- `shopIds` (array) — obbligatorio [default: []]\n\n**Risposte di successo:**\n- **200**: Assegna negozi request shops al catalogo.\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** PrizesCatalogs.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["shopIds"],"additionalProperties":true,"properties":{"shopIds":{"type":"array","items":{"type":"integer"},"default":[]}}}}},"required":true},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"catalogId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Assegna negozi request shops al catalogo.","content":{"application/json":{"schema":{"description":"Assegna negozi request shops al catalogo.","type":"object","additionalProperties":true,"example":{"shopIds":[42,55]}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/prizes-catalogs/{catalogId}/pickup-shops":{"get":{"tags":["PrizesCatalogs"],"description":"Elenco paginato di pickup-shops del programma indicato in `programId`.\n\n**Contesto:** I **Cataloghi premi** raggruppano i premi disponibili al riscatto. Possono richiedere conferma backoffice e regolare visibilità per tier o audience.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n- `catalogId` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Negozi pickup shops del catalogo premi.\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** PrizesCatalogs.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"catalogId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Negozi pickup shops del catalogo premi.","content":{"application/json":{"schema":{"description":"Negozi pickup shops del catalogo premi.","type":"object","additionalProperties":true,"example":{"content":[{"id":10,"shopId":42,"shopName":"Negozio centro","address":{"id":7,"street":"Via Roma","number":"12","floor":null,"apartment":null,"zipCode":"20121","city":"Milano","state":"MI","country":"Italia","countryId":1,"latitude":45.4642,"longitude":9.19,"createdAt":"2026-05-15T10:00:00.000Z","updatedAt":"2026-05-15T10:00:00.000Z"},"openingHours":[{"day":"MONDAY","ranges":[{"start":"09:00","end":"19:00"}]},{"day":"TUESDAY","ranges":[{"start":"09:00","end":"19:00"}]}],"contact":{"phone":"+39 02 1234567","email":"centro@example.com","website":"https://example.com","ecommerce":null,"contactPerson":"Mario Rossi","taxNumber":"12345678901"}}]}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"put":{"tags":["PrizesCatalogs"],"description":"Aggiorna una programma fedeltà (campi consentiti) del programma indicato in `programId`.\n\n**Contesto:** I **Cataloghi premi** raggruppano i premi disponibili al riscatto. Possono richiedere conferma backoffice e regolare visibilità per tier o audience.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n- `catalogId` (integer) — obbligatorio\n\n**Body JSON:**\n- `shopIds` (array) — obbligatorio [default: []]\n\n**Risposte di successo:**\n- **200**: Assegna negozi pickup shops al catalogo.\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** PrizesCatalogs.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["shopIds"],"additionalProperties":true,"properties":{"shopIds":{"type":"array","items":{"type":"integer"},"default":[]}}}}},"required":true},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"catalogId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Assegna negozi pickup shops al catalogo.","content":{"application/json":{"schema":{"description":"Assegna negozi pickup shops al catalogo.","type":"object","additionalProperties":true,"example":{"shopIds":[42,55]}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/prizes-catalogs/{catalogId}/visibility":{"get":{"tags":["PrizesCatalogs"],"description":"Get visibility clusters (audiences/tags/tiers) for a prize catalog. Combined in OR.\n\n**Contesto:** I **Cataloghi premi** raggruppano i premi disponibili al riscatto. Possono richiedere conferma backoffice e regolare visibilità per tier o audience.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n- `catalogId` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Visibilità catalogo (audience, tag, tier in OR).\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** PrizesCatalogs.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"catalogId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Visibilità catalogo (audience, tag, tier in OR).","content":{"application/json":{"schema":{"description":"Visibilità catalogo (audience, tag, tier in OR).","type":"object","additionalProperties":true,"example":{"audienceIds":[1,2],"tagIds":[5],"tierIds":[]}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"put":{"tags":["PrizesCatalogs"],"description":"Replace visibility clusters (audiences/tags/tiers). All three lists are optional.\n\n**Contesto:** I **Cataloghi premi** raggruppano i premi disponibili al riscatto. Possono richiedere conferma backoffice e regolare visibilità per tier o audience.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n- `catalogId` (integer) — obbligatorio\n\n**Body JSON:**\n- `audienceIds` (array) — opzionale\n- `tagIds` (array) — opzionale\n- `tierIds` (array) — opzionale\n\n**Risposte di successo:**\n- **200**: Aggiorna visibilità catalogo.\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** PrizesCatalogs.","requestBody":{"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"properties":{"audienceIds":{"type":"array","items":{"type":"integer"}},"tagIds":{"type":"array","items":{"type":"integer"}},"tierIds":{"type":"array","items":{"type":"integer"}}}}}}},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"catalogId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Aggiorna visibilità catalogo.","content":{"application/json":{"schema":{"description":"Aggiorna visibilità catalogo.","type":"object","additionalProperties":true,"example":{"audienceIds":[1],"tagIds":[5],"tierIds":[6]}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/prizes-catalogs/{catalogId}/fulfillment-steps":{"get":{"tags":["PrizesCatalogs"],"description":"Elenco paginato di fulfillment-steps del programma indicato in `programId`.\n\n**Contesto:** I **Cataloghi premi** raggruppano i premi disponibili al riscatto. Possono richiedere conferma backoffice e regolare visibilità per tier o audience.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n- `catalogId` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Step intermedi di fulfillment del catalogo.\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** PrizesCatalogs.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"catalogId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Step intermedi di fulfillment del catalogo.","content":{"application/json":{"schema":{"description":"Step intermedi di fulfillment del catalogo.","type":"object","additionalProperties":true,"example":{"content":[{"id":3,"sortOrder":0,"code":"PACKING","name":"Imballaggio","description":null}]}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"post":{"tags":["PrizesCatalogs"],"description":"Crea fulfillment-steps del programma indicato in `programId`.\n\n**Contesto:** I **Cataloghi premi** raggruppano i premi disponibili al riscatto. Possono richiedere conferma backoffice e regolare visibilità per tier o audience.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n- `catalogId` (integer) — obbligatorio\n\n**Body JSON:**\n- `code` (string) — obbligatorio\n- `name` (string) — obbligatorio\n- `description` (string) — opzionale\n- `sortOrder` (integer) — opzionale [default: 0]\n\n**Risposte di successo:**\n- **201**: Crea step fulfillment.\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** PrizesCatalogs.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["code","name"],"additionalProperties":true,"properties":{"code":{"type":"string","maxLength":64},"name":{"type":"string"},"description":{"type":"string"},"sortOrder":{"type":"integer","default":0}}}}},"required":true},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"catalogId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"201":{"description":"Crea step fulfillment.","content":{"application/json":{"schema":{"description":"Crea step fulfillment.","type":"object","additionalProperties":true,"example":{"id":3,"sortOrder":0,"code":"PACKING","name":"Imballaggio","description":null}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/prizes-catalogs/{catalogId}/fulfillment-steps/{stepId}":{"delete":{"tags":["PrizesCatalogs"],"description":"Elimina una programma fedeltà del programma indicato in `programId` (soft delete o `TERMINATED` se vincoli FK).\n\n**Contesto:** I **Cataloghi premi** raggruppano i premi disponibili al riscatto. Possono richiedere conferma backoffice e regolare visibilità per tier o audience.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n- `catalogId` (integer) — obbligatorio\n- `stepId` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **204**: Step fulfillment eliminato.\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** PrizesCatalogs.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"catalogId","required":true},{"schema":{"type":"integer"},"in":"path","name":"stepId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"204":{"description":"Step fulfillment eliminato."},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/prizes-catalogs/{catalogId}/variant-types":{"get":{"tags":["PrizesCatalogs"],"description":"Elenco paginato di variant-types del programma indicato in `programId`.\n\n**Contesto:** I **Cataloghi premi** raggruppano i premi disponibili al riscatto. Possono richiedere conferma backoffice e regolare visibilità per tier o audience.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n- `catalogId` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Tipi variante del catalogo con valori.\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** PrizesCatalogs.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"catalogId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Tipi variante del catalogo con valori.","content":{"application/json":{"schema":{"description":"Tipi variante del catalogo con valori.","type":"object","additionalProperties":true,"example":{"content":[{"id":1,"name":"Taglia","dictionaryKey":"size","sortOrder":0,"values":[{"id":10,"value":"M","dictionaryKey":"size_m","sortOrder":1}]}]}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"post":{"tags":["PrizesCatalogs"],"description":"Crea variant-types del programma indicato in `programId`.\n\n**Contesto:** I **Cataloghi premi** raggruppano i premi disponibili al riscatto. Possono richiedere conferma backoffice e regolare visibilità per tier o audience.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n- `catalogId` (integer) — obbligatorio\n\n**Body JSON:**\n- `name` (string) — obbligatorio\n- `dictionaryKey` (string) — opzionale\n- `sortOrder` (integer) — opzionale [default: 0]\n\n**Risposte di successo:**\n- **201**: Crea tipo variante.\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** PrizesCatalogs.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["name"],"additionalProperties":true,"properties":{"name":{"type":"string"},"dictionaryKey":{"type":"string"},"sortOrder":{"type":"integer","default":0}}}}},"required":true},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"catalogId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"201":{"description":"Crea tipo variante.","content":{"application/json":{"schema":{"description":"Crea tipo variante.","type":"object","additionalProperties":true,"example":{"id":1,"name":"Taglia","dictionaryKey":"size","sortOrder":0,"values":[]}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/prizes-catalogs/{catalogId}/variant-types/{typeId}/values":{"post":{"tags":["PrizesCatalogs"],"description":"Crea values del programma indicato in `programId`.\n\n**Contesto:** I **Cataloghi premi** raggruppano i premi disponibili al riscatto. Possono richiedere conferma backoffice e regolare visibilità per tier o audience.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n- `catalogId` (integer) — obbligatorio\n- `typeId` (integer) — obbligatorio\n\n**Body JSON:**\n- `value` (string) — obbligatorio\n- `dictionaryKey` (string) — opzionale\n- `sortOrder` (integer) — opzionale [default: 0]\n\n**Risposte di successo:**\n- **201**: Aggiunge valore a tipo variante.\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** PrizesCatalogs.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["value"],"additionalProperties":true,"properties":{"value":{"type":"string"},"dictionaryKey":{"type":"string"},"sortOrder":{"type":"integer","default":0}}}}},"required":true},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"catalogId","required":true},{"schema":{"type":"integer"},"in":"path","name":"typeId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"201":{"description":"Aggiunge valore a tipo variante.","content":{"application/json":{"schema":{"description":"Aggiunge valore a tipo variante.","type":"object","additionalProperties":true,"example":{"id":10,"value":"M","dictionaryKey":"size_m","sortOrder":1}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/prizes-catalogs/{catalogId}/variant-types/{typeId}/values/{valueId}":{"delete":{"tags":["PrizesCatalogs"],"description":"Elimina una programma fedeltà del programma indicato in `programId` (soft delete o `TERMINATED` se vincoli FK).\n\n**Contesto:** I **Cataloghi premi** raggruppano i premi disponibili al riscatto. Possono richiedere conferma backoffice e regolare visibilità per tier o audience.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n- `catalogId` (integer) — obbligatorio\n- `typeId` (integer) — obbligatorio\n- `valueId` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **204**: Valore variante eliminato.\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** PrizesCatalogs.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"catalogId","required":true},{"schema":{"type":"integer"},"in":"path","name":"typeId","required":true},{"schema":{"type":"integer"},"in":"path","name":"valueId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"204":{"description":"Valore variante eliminato."},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/prizes-catalogs/{catalogId}/personalization-types":{"get":{"tags":["PrizesCatalogs"],"description":"Elenco paginato di personalization-types del programma indicato in `programId`.\n\n**Contesto:** I **Cataloghi premi** raggruppano i premi disponibili al riscatto. Possono richiedere conferma backoffice e regolare visibilità per tier o audience.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n- `catalogId` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Tipi personalizzazione del catalogo.\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** PrizesCatalogs.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"catalogId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Tipi personalizzazione del catalogo.","content":{"application/json":{"schema":{"description":"Tipi personalizzazione del catalogo.","type":"object","additionalProperties":true,"example":{"content":[{"id":1,"name":"Taglia","dictionaryKey":"size","sortOrder":0,"values":[{"id":10,"value":"M","dictionaryKey":"size_m","sortOrder":1}]}]}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"post":{"tags":["PrizesCatalogs"],"description":"Crea personalization-types del programma indicato in `programId`.\n\n**Contesto:** I **Cataloghi premi** raggruppano i premi disponibili al riscatto. Possono richiedere conferma backoffice e regolare visibilità per tier o audience.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n- `catalogId` (integer) — obbligatorio\n\n**Body JSON:**\n- `name` (string) — obbligatorio\n- `dictionaryKey` (string) — opzionale\n- `sortOrder` (integer) — opzionale [default: 0]\n\n**Risposte di successo:**\n- **201**: Crea tipo personalizzazione.\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** PrizesCatalogs.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["name"],"additionalProperties":true,"properties":{"name":{"type":"string"},"dictionaryKey":{"type":"string"},"sortOrder":{"type":"integer","default":0}}}}},"required":true},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"catalogId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"201":{"description":"Crea tipo personalizzazione.","content":{"application/json":{"schema":{"description":"Crea tipo personalizzazione.","type":"object","additionalProperties":true,"example":{"id":1,"name":"Taglia","dictionaryKey":"size","sortOrder":0,"values":[]}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/prizes-catalogs/{catalogId}/personalization-types/{typeId}/values":{"post":{"tags":["PrizesCatalogs"],"description":"Crea values del programma indicato in `programId`.\n\n**Contesto:** I **Cataloghi premi** raggruppano i premi disponibili al riscatto. Possono richiedere conferma backoffice e regolare visibilità per tier o audience.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n- `catalogId` (integer) — obbligatorio\n- `typeId` (integer) — obbligatorio\n\n**Body JSON:**\n- `value` (string) — obbligatorio\n- `dictionaryKey` (string) — opzionale\n- `sortOrder` (integer) — opzionale [default: 0]\n\n**Risposte di successo:**\n- **201**: Aggiunge valore personalizzazione.\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** PrizesCatalogs.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["value"],"additionalProperties":true,"properties":{"value":{"type":"string"},"dictionaryKey":{"type":"string"},"sortOrder":{"type":"integer","default":0}}}}},"required":true},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"catalogId","required":true},{"schema":{"type":"integer"},"in":"path","name":"typeId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"201":{"description":"Aggiunge valore personalizzazione.","content":{"application/json":{"schema":{"description":"Aggiunge valore personalizzazione.","type":"object","additionalProperties":true,"example":{"id":20,"value":"Mario","dictionaryKey":"name_mario","sortOrder":0}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/prizes/{prizeId}/request-shops":{"get":{"tags":["Prizes"],"description":"Elenco paginato di request-shops.\n\n**Contesto:** I **Premi** sono ricompense riscattabili con wallet/punti o pagamento misto. Hanno tipo (fisico, digitale), stock, opzioni di pagamento e fulfillment (ritiro, spedizione).\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `prizeId` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Negozi request shops effettivi del premio (lista risolta; override = solo righe premio).\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Prizes.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"prizeId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Negozi request shops effettivi del premio (lista risolta; override = solo righe premio).","content":{"application/json":{"schema":{"description":"Negozi request shops effettivi del premio (lista risolta; override = solo righe premio).","type":"object","additionalProperties":true,"example":{"allowsAll":true,"resolvedMode":"ALL","override":[],"content":[{"shopId":42,"shopName":"Negozio centro"}]}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"put":{"tags":["Prizes"],"description":"Aggiorna request-shops (campi consentiti).\n\n**Contesto:** I **Premi** sono ricompense riscattabili con wallet/punti o pagamento misto. Hanno tipo (fisico, digitale), stock, opzioni di pagamento e fulfillment (ritiro, spedizione).\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `prizeId` (integer) — obbligatorio\n\n**Body JSON:**\n- `shopIds` (array) — obbligatorio [default: []]\n\n**Risposte di successo:**\n- **200**: Assegna negozi request shops al premio.\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Prizes.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["shopIds"],"additionalProperties":true,"properties":{"shopIds":{"type":"array","items":{"type":"integer"},"default":[]}}}}},"required":true},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"prizeId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Assegna negozi request shops al premio.","content":{"application/json":{"schema":{"description":"Assegna negozi request shops al premio.","type":"object","additionalProperties":true,"example":{"shopIds":[42]}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/prizes/{prizeId}/pickup-shops":{"get":{"tags":["Prizes"],"description":"Elenco paginato di pickup-shops.\n\n**Contesto:** I **Premi** sono ricompense riscattabili con wallet/punti o pagamento misto. Hanno tipo (fisico, digitale), stock, opzioni di pagamento e fulfillment (ritiro, spedizione).\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `prizeId` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Negozi pickup shops effettivi del premio (lista risolta; override = solo righe premio).\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Prizes.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"prizeId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Negozi pickup shops effettivi del premio (lista risolta; override = solo righe premio).","content":{"application/json":{"schema":{"description":"Negozi pickup shops effettivi del premio (lista risolta; override = solo righe premio).","type":"object","additionalProperties":true,"example":{"allowsAll":true,"resolvedMode":"ALL","override":[],"content":[{"shopId":42,"shopName":"Negozio centro","address":{"id":7,"street":"Via Roma","number":"12","floor":null,"apartment":null,"zipCode":"20121","city":"Milano","state":"MI","country":"Italia","countryId":1,"latitude":45.4642,"longitude":9.19,"createdAt":"2026-05-15T10:00:00.000Z","updatedAt":"2026-05-15T10:00:00.000Z"},"openingHours":[{"day":"MONDAY","ranges":[{"start":"09:00","end":"19:00"}]},{"day":"TUESDAY","ranges":[{"start":"09:00","end":"19:00"}]}],"contact":{"phone":"+39 02 1234567","email":"centro@example.com","website":"https://example.com","ecommerce":null,"contactPerson":"Mario Rossi","taxNumber":"12345678901"}}]}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"put":{"tags":["Prizes"],"description":"Aggiorna pickup-shops (campi consentiti).\n\n**Contesto:** I **Premi** sono ricompense riscattabili con wallet/punti o pagamento misto. Hanno tipo (fisico, digitale), stock, opzioni di pagamento e fulfillment (ritiro, spedizione).\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `prizeId` (integer) — obbligatorio\n\n**Body JSON:**\n- `shopIds` (array) — obbligatorio [default: []]\n\n**Risposte di successo:**\n- **200**: Assegna negozi pickup shops al premio.\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Prizes.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["shopIds"],"additionalProperties":true,"properties":{"shopIds":{"type":"array","items":{"type":"integer"},"default":[]}}}}},"required":true},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"prizeId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Assegna negozi pickup shops al premio.","content":{"application/json":{"schema":{"description":"Assegna negozi pickup shops al premio.","type":"object","additionalProperties":true,"example":{"shopIds":[42]}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/prizes/{prizeId}/variant-stocks":{"get":{"tags":["Prizes"],"description":"Elenco paginato di variant-stocks.\n\n**Contesto:** I **Premi** sono ricompense riscattabili con wallet/punti o pagamento misto. Hanno tipo (fisico, digitale), stock, opzioni di pagamento e fulfillment (ritiro, spedizione).\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `prizeId` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Stock per combinazione varianti del premio.\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Prizes.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"prizeId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Stock per combinazione varianti del premio.","content":{"application/json":{"schema":{"description":"Stock per combinazione varianti del premio.","type":"object","additionalProperties":true,"example":{"content":[{"id":7,"comboSignature":"1:10|2:20","stockQuantity":15,"combination":[{"typeId":1,"valueId":10,"typeName":"Taglia","value":"M"}],"createdAt":"2026-05-15T10:00:00.000Z","updatedAt":"2026-05-15T10:00:00.000Z"}]}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"post":{"tags":["Prizes"],"description":"Crea variant-stocks.\n\n**Contesto:** I **Premi** sono ricompense riscattabili con wallet/punti o pagamento misto. Hanno tipo (fisico, digitale), stock, opzioni di pagamento e fulfillment (ritiro, spedizione).\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `prizeId` (integer) — obbligatorio\n\n**Body JSON:**\n- `combination` (array) — obbligatorio\n- `stockQuantity` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **201**: Crea/aggiorna stock combinazione varianti.\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Prizes.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["combination","stockQuantity"],"additionalProperties":true,"properties":{"combination":{"type":"array","minItems":1,"items":{"type":"object","required":["typeId","valueId"],"properties":{"typeId":{"type":"integer"},"valueId":{"type":"integer"}}}},"stockQuantity":{"type":"integer","minimum":0}}}}},"required":true},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"prizeId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"201":{"description":"Crea/aggiorna stock combinazione varianti.","content":{"application/json":{"schema":{"description":"Crea/aggiorna stock combinazione varianti.","type":"object","additionalProperties":true,"example":{"id":7,"comboSignature":"1:10","stockQuantity":15,"combination":[{"typeId":1,"valueId":10}],"createdAt":"2026-05-15T10:00:00.000Z","updatedAt":"2026-05-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/prizes/{prizeId}/variant-stocks/{stockId}":{"delete":{"tags":["Prizes"],"description":"Elimina variant-stocks (soft delete o `TERMINATED` se vincoli FK).\n\n**Contesto:** I **Premi** sono ricompense riscattabili con wallet/punti o pagamento misto. Hanno tipo (fisico, digitale), stock, opzioni di pagamento e fulfillment (ritiro, spedizione).\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `prizeId` (integer) — obbligatorio\n- `stockId` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **204**: Stock variante eliminato.\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Prizes.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"prizeId","required":true},{"schema":{"type":"integer"},"in":"path","name":"stockId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"204":{"description":"Stock variante eliminato."},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/prizes/{prizeId}/personalizations":{"get":{"tags":["Prizes"],"description":"Elenco paginato di personalizations.\n\n**Contesto:** I **Premi** sono ricompense riscattabili con wallet/punti o pagamento misto. Hanno tipo (fisico, digitale), stock, opzioni di pagamento e fulfillment (ritiro, spedizione).\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `prizeId` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Personalizzazioni applicabili al premio.\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Prizes.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"prizeId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Personalizzazioni applicabili al premio.","content":{"application/json":{"schema":{"description":"Personalizzazioni applicabili al premio.","type":"object","additionalProperties":true,"example":{"content":[{"typeId":2,"typeName":"Nome","typeDictionaryKey":"name","valueId":20,"value":"Mario","valueDictionaryKey":"name_mario","sortOrder":0}]}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"put":{"tags":["Prizes"],"description":"Aggiorna personalizations (campi consentiti).\n\n**Contesto:** I **Premi** sono ricompense riscattabili con wallet/punti o pagamento misto. Hanno tipo (fisico, digitale), stock, opzioni di pagamento e fulfillment (ritiro, spedizione).\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `prizeId` (integer) — obbligatorio\n\n**Body JSON:**\n- `valueIds` (array) — obbligatorio [default: []]\n\n**Risposte di successo:**\n- **200**: Assegna valori personalizzazione al premio.\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Prizes.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["valueIds"],"additionalProperties":true,"properties":{"valueIds":{"type":"array","items":{"type":"integer"},"default":[]}}}}},"required":true},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"prizeId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Assegna valori personalizzazione al premio.","content":{"application/json":{"schema":{"description":"Assegna valori personalizzazione al premio.","type":"object","additionalProperties":true,"example":{"valueIds":[20]}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/prizes/{prizeId}/payment-options":{"get":{"tags":["Prizes"],"description":"List payment options for a prize (each option is an OR alternative with wallet and/or money on the same row).\n\n**Contesto:** I **Premi** sono ricompense riscattabili con wallet/punti o pagamento misto. Hanno tipo (fisico, digitale), stock, opzioni di pagamento e fulfillment (ritiro, spedizione).\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `prizeId` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Opzioni di pagamento del premio.\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Prizes.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"prizeId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Opzioni di pagamento del premio.","content":{"application/json":{"schema":{"description":"Opzioni di pagamento del premio.","type":"object","additionalProperties":true,"example":{"content":[{"id":100,"name":"Punti + supplemento","sortOrder":0,"walletId":1,"walletUnits":500,"moneyAmount":9.99,"currencyId":1,"createdAt":"2026-05-15T10:00:00.000Z","updatedAt":"2026-05-15T10:00:00.000Z"}]}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"post":{"tags":["Prizes"],"description":"Create a payment option (OR alternative). Wallet and money on the same object (not an items array). Legacy body with `items[]` is merged into one row.\n\n**Contesto:** I **Premi** sono ricompense riscattabili con wallet/punti o pagamento misto. Hanno tipo (fisico, digitale), stock, opzioni di pagamento e fulfillment (ritiro, spedizione).\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `prizeId` (integer) — obbligatorio\n\n**Body JSON:**\n- `name` (string) — opzionale\n- `sortOrder` (integer) — opzionale [default: 0]\n- `walletId` (integer) — opzionale\n- `walletUnits` (number) — opzionale\n- `moneyAmount` (number) — opzionale\n- `currencyId` (integer) — opzionale\n- `items` (array) — opzionale: Deprecated: merged into a single payment row if sent.\n\n**Risposte di successo:**\n- **201**: Crea opzione di pagamento premio.\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Prizes.","requestBody":{"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"properties":{"name":{"type":"string"},"sortOrder":{"type":"integer","default":0},"walletId":{"type":"integer","nullable":true},"walletUnits":{"type":"number","minimum":0.01,"nullable":true},"moneyAmount":{"type":"number","minimum":0.01,"nullable":true},"currencyId":{"type":"integer","nullable":true},"items":{"type":"array","description":"Deprecated: merged into a single payment row if sent.","items":{"type":"object","additionalProperties":true}}}}}}},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"prizeId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"201":{"description":"Crea opzione di pagamento premio.","content":{"application/json":{"schema":{"description":"Crea opzione di pagamento premio.","type":"object","additionalProperties":true,"example":{"id":100,"name":"Punti + supplemento","sortOrder":0,"walletId":1,"walletUnits":500,"moneyAmount":9.99,"currencyId":1,"createdAt":"2026-05-15T10:00:00.000Z","updatedAt":"2026-05-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/prizes/{prizeId}/payment-options/{optionId}":{"delete":{"tags":["Prizes"],"description":"Elimina payment-options (soft delete o `TERMINATED` se vincoli FK).\n\n**Contesto:** I **Premi** sono ricompense riscattabili con wallet/punti o pagamento misto. Hanno tipo (fisico, digitale), stock, opzioni di pagamento e fulfillment (ritiro, spedizione).\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `prizeId` (integer) — obbligatorio\n- `optionId` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **204**: Opzione pagamento eliminata.\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Prizes.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"prizeId","required":true},{"schema":{"type":"integer"},"in":"path","name":"optionId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"204":{"description":"Opzione pagamento eliminata."},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/prizes/{id}/request":{"post":{"tags":["PrizeRequests"],"description":"Crea request.\n\n**Contesto:** Le **Richieste premio** tracciano il ciclo di vita del riscatto: richiesta → conferma → fulfillment → ritiro/consegna → eventuale annullo o rimborso Stripe.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `id` (integer) — obbligatorio\n\n**Body JSON:**\n- `customerId` (integer) — obbligatorio\n- `quantity` (integer) — opzionale [default: 1]\n- `paymentOptionId` (integer) — obbligatorio\n- `variants` (array) — opzionale\n- `personalizations` (array) — opzionale\n- `delivery` (object) — obbligatorio\n- `requestShopId` (integer) — opzionale\n- `requestTerminalId` (integer) — opzionale\n- `notes` (string) — opzionale\n- `voucherCodes` (array) — opzionale: Optional prize vouchers (voucher_kind=PRIZE) to discount wallet units\n\n**Risposte di successo:**\n- **201**: Crea richiesta premio (wallet/denaro/voucher).\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** PrizeRequests.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["customerId","paymentOptionId","delivery"],"additionalProperties":true,"properties":{"customerId":{"type":"integer"},"quantity":{"type":"integer","minimum":1,"default":1},"paymentOptionId":{"type":"integer"},"variants":{"type":"array","items":{"type":"object","required":["typeId","valueId"],"properties":{"typeId":{"type":"integer"},"valueId":{"type":"integer"}}}},"personalizations":{"type":"array","items":{"type":"object","required":["valueId"],"properties":{"typeId":{"type":"integer"},"valueId":{"type":"integer"}}}},"delivery":{"type":"object","required":["mode"],"additionalProperties":true,"properties":{"mode":{"type":"string","enum":["PICKUP_SHOP","HOME_DELIVERY"]},"pickupShopId":{"type":"integer"},"address":{"type":"object","additionalProperties":true,"properties":{"recipientName":{"type":"string"},"phone":{"type":"string"},"street":{"type":"string"},"number":{"type":"string"},"floor":{"type":"string"},"apartment":{"type":"string"},"zipCode":{"type":"string"},"city":{"type":"string"},"state":{"type":"string"},"country":{"type":"string"},"notes":{"type":"string","description":"Note di consegna (istruzioni per il corriere, orari, ecc.)"}}}}},"requestShopId":{"type":"integer","nullable":true},"requestTerminalId":{"type":"integer","nullable":true},"notes":{"type":"string"},"voucherCodes":{"type":"array","items":{"type":"string"},"description":"Optional prize vouchers (voucher_kind=PRIZE) to discount wallet units"}}}}},"required":true},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"201":{"description":"Crea richiesta premio (wallet/denaro/voucher).","content":{"application/json":{"schema":{"description":"Crea richiesta premio (wallet/denaro/voucher).","type":"object","additionalProperties":true,"example":{"id":1203,"status":"PENDING_PAYMENT","moneyOwed":19.98,"movementId":9001,"paymentSnapshot":{"optionId":100,"walletId":1,"walletUnits":500,"moneyAmount":null,"currencyId":null,"quantity":1,"moneyOwed":0,"appliedPrizeVouchers":[]},"autoConfirmed":false}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/prize-requests/{id}/finalize-payment":{"post":{"tags":["PrizeRequests"],"description":"Finalize payment for a PENDING_PAYMENT request (placeholder; replaced by Stripe webhook in phase 5).\n\n**Contesto:** Le **Richieste premio** tracciano il ciclo di vita del riscatto: richiesta → conferma → fulfillment → ritiro/consegna → eventuale annullo o rimborso Stripe.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `id` (integer) — obbligatorio\n\n**Body JSON:**\n- `paymentReference` (string) — opzionale\n\n**Risposte di successo:**\n- **200**: Finalizza pagamento richiesta premio.\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** PrizeRequests.","requestBody":{"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"properties":{"paymentReference":{"type":"string"}}}}}},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Finalizza pagamento richiesta premio.","content":{"application/json":{"schema":{"description":"Finalizza pagamento richiesta premio.","type":"object","additionalProperties":true,"example":{"id":1203,"status":"REQUESTED","movementId":9001,"autoConfirmed":true}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/prize-requests/dashboard":{"get":{"tags":["PrizeRequests"],"description":"Elenco paginato di dashboard del programma indicato in `programId`.\n\n**Contesto:** Le **Richieste premio** tracciano il ciclo di vita del riscatto: richiesta → conferma → fulfillment → ritiro/consegna → eventuale annullo o rimborso Stripe.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n\n**Querystring:**\n- `catalogId` (integer) — opzionale\n- `period` (string) — opzionale (valori: 7, 30, 90, ytd, custom) [default: \"30\"]\n- `fromDate` (string) — opzionale\n- `toDate` (string) — opzionale\n\n**Risposte di successo:**\n- **200**: Metriche aggregate dashboard richieste premio (KPI, funnel, trend, coda conferma).\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** PrizeRequests.","parameters":[{"schema":{"type":"integer"},"in":"query","name":"catalogId","required":false},{"schema":{"type":"string","enum":["7","30","90","ytd","custom"],"default":"30"},"in":"query","name":"period","required":false},{"schema":{"type":"string"},"in":"query","name":"fromDate","required":false},{"schema":{"type":"string"},"in":"query","name":"toDate","required":false},{"schema":{"type":"integer"},"in":"path","name":"programId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Metriche aggregate dashboard richieste premio (KPI, funnel, trend, coda conferma).","content":{"application/json":{"schema":{"description":"Metriche aggregate dashboard richieste premio (KPI, funnel, trend, coda conferma).","type":"object","additionalProperties":true,"example":{"kpi":{"requested":42,"confirmed":38,"collected":30,"cancelled":2},"funnel":[{"step":"REQUESTED","count":10}],"trend":[{"date":"2026-05-01","count":5}],"pendingConfirmation":4}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/prize-requests":{"get":{"tags":["PrizeRequests"],"description":"Elenco paginato di richiesta premio del programma indicato in `programId`.\n\n**Contesto:** Le **Richieste premio** tracciano il ciclo di vita del riscatto: richiesta → conferma → fulfillment → ritiro/consegna → eventuale annullo o rimborso Stripe.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n\n**Querystring:**\n- `status` (string) — opzionale (valori: PENDING_PAYMENT, REQUESTED, CONFIRMED, IN_FULFILLMENT, DELIVERY_SCHEDULED, OUT_FOR_DELIVERY, AWAITING_CUSTOMER_CONFIRMATION, READY_FOR_PICKUP, COLLECTED, NOT_PICKED_UP, RETURNED, CANCELLED)\n- `customerId` (integer) — opzionale\n- `prizeId` (integer) — opzionale\n- `catalogId` (integer) — opzionale\n- `shopId` (integer) — opzionale\n- `fromDate` (string) — opzionale\n- `toDate` (string) — opzionale\n- `pendingConfirmation` (boolean) — opzionale\n- `includeClosed` (boolean) — opzionale\n- `page` (integer) — opzionale [default: 1]\n- `size` (integer) — opzionale [default: 20]\n\n**Risposte di successo:**\n- **200**: Richieste premio del programma\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** PrizeRequests.","parameters":[{"schema":{"type":"string","enum":["PENDING_PAYMENT","REQUESTED","CONFIRMED","IN_FULFILLMENT","DELIVERY_SCHEDULED","OUT_FOR_DELIVERY","AWAITING_CUSTOMER_CONFIRMATION","READY_FOR_PICKUP","COLLECTED","NOT_PICKED_UP","RETURNED","CANCELLED"]},"in":"query","name":"status","required":false},{"schema":{"type":"integer"},"in":"query","name":"customerId","required":false},{"schema":{"type":"integer"},"in":"query","name":"prizeId","required":false},{"schema":{"type":"integer"},"in":"query","name":"catalogId","required":false},{"schema":{"type":"integer"},"in":"query","name":"shopId","required":false},{"schema":{"type":"string"},"in":"query","name":"fromDate","required":false},{"schema":{"type":"string"},"in":"query","name":"toDate","required":false},{"schema":{"type":"boolean"},"in":"query","name":"pendingConfirmation","required":false},{"schema":{"type":"boolean"},"in":"query","name":"includeClosed","required":false},{"schema":{"type":"integer","default":1},"in":"query","name":"page","required":false},{"schema":{"type":"integer","default":20},"in":"query","name":"size","required":false},{"schema":{"type":"integer"},"in":"path","name":"programId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Richieste premio del programma","content":{"application/json":{"schema":{"description":"Richieste premio del programma","type":"object","additionalProperties":true,"example":{"content":[{"id":1203,"programId":1,"prizeId":50,"prizeName":"Maglietta","prizeCode":"SKU-001","description":"Maglietta ufficiale in cotone","descriptionLong":"<p>Maglietta ufficiale del programma fedeltà, 100% cotone.</p>","imageUrl":"https://cdn.example/prize-cover.jpg","images":[{"id":1,"url":"https://cdn.example/prize-cover.jpg","sortOrder":0},{"id":2,"url":"https://cdn.example/prize-detail.jpg","sortOrder":1}],"catalogId":10,"catalogName":"Catalogo estivo","customerId":200,"customerCardId":301,"quantity":1,"status":"REQUESTED","fulfillmentStepCode":null,"deliveryMode":"PICKUP_SHOP","pickupShopId":42,"shippingAddress":null,"deliveryAddress":null,"shippingTrackingNumber":null,"fulfillmentStepName":null,"paymentOptionId":100,"paymentSnapshot":{"optionId":100,"walletId":1,"walletUnits":500,"moneyAmount":null,"currencyId":null,"quantity":1,"moneyOwed":0,"appliedPrizeVouchers":[]},"requestShopId":42,"requestTerminalId":null,"requestOperatorId":null,"confirmedByOperatorId":null,"stripePaymentIntentId":null,"stripePaymentStatus":null,"requestedAt":"2026-05-15T10:00:00.000Z","pendingPaymentAt":null,"confirmedAt":null,"fulfillmentStartedAt":null,"collectedAt":null,"cancelledAt":null,"cancelReason":null,"notes":null,"createdAt":"2026-05-15T10:00:00.000Z","updatedAt":"2026-05-15T10:00:00.000Z","variants":[{"typeId":1,"valueId":10,"typeName":"Taglia","value":"M"}],"personalizations":[{"typeId":2,"valueId":20,"typeName":"Nome","value":"Mario"}],"payments":[{"id":1,"walletId":1,"walletUnits":500,"moneyAmount":null,"currencyId":null,"voucherId":null,"walletTransactionId":8001,"movementId":9001,"createdAt":"2026-05-15T10:00:00.000Z"}],"catalogRequiresConfirmation":true}],"totalElements":1,"totalPages":1,"page":1,"size":20}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/customers/{customerId}/prize-requests":{"get":{"tags":["PrizeRequests"],"description":"Elenco paginato di richiesta premio.\n\n**Contesto:** Le **Richieste premio** tracciano il ciclo di vita del riscatto: richiesta → conferma → fulfillment → ritiro/consegna → eventuale annullo o rimborso Stripe.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `customerId` (integer) — obbligatorio\n\n**Querystring:**\n- `status` (string) — opzionale (valori: PENDING_PAYMENT, REQUESTED, CONFIRMED, IN_FULFILLMENT, DELIVERY_SCHEDULED, OUT_FOR_DELIVERY, AWAITING_CUSTOMER_CONFIRMATION, READY_FOR_PICKUP, COLLECTED, NOT_PICKED_UP, RETURNED, CANCELLED)\n- `page` (integer) — opzionale [default: 1]\n- `size` (integer) — opzionale [default: 20]\n\n**Risposte di successo:**\n- **200**: Richieste premio del cliente\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** PrizeRequests.","parameters":[{"schema":{"type":"string","enum":["PENDING_PAYMENT","REQUESTED","CONFIRMED","IN_FULFILLMENT","DELIVERY_SCHEDULED","OUT_FOR_DELIVERY","AWAITING_CUSTOMER_CONFIRMATION","READY_FOR_PICKUP","COLLECTED","NOT_PICKED_UP","RETURNED","CANCELLED"]},"in":"query","name":"status","required":false},{"schema":{"type":"integer","default":1},"in":"query","name":"page","required":false},{"schema":{"type":"integer","default":20},"in":"query","name":"size","required":false},{"schema":{"type":"integer"},"in":"path","name":"customerId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Richieste premio del cliente","content":{"application/json":{"schema":{"description":"Richieste premio del cliente","type":"object","additionalProperties":true,"example":{"content":[{"id":1203,"programId":1,"prizeId":50,"prizeName":"Maglietta","prizeCode":"SKU-001","description":"Maglietta ufficiale in cotone","descriptionLong":"<p>Maglietta ufficiale del programma fedeltà, 100% cotone.</p>","imageUrl":"https://cdn.example/prize-cover.jpg","images":[{"id":1,"url":"https://cdn.example/prize-cover.jpg","sortOrder":0},{"id":2,"url":"https://cdn.example/prize-detail.jpg","sortOrder":1}],"catalogId":10,"catalogName":"Catalogo estivo","customerId":200,"customerCardId":301,"quantity":1,"status":"REQUESTED","fulfillmentStepCode":null,"deliveryMode":"PICKUP_SHOP","pickupShopId":42,"shippingAddress":null,"deliveryAddress":null,"shippingTrackingNumber":null,"fulfillmentStepName":null,"paymentOptionId":100,"paymentSnapshot":{"optionId":100,"walletId":1,"walletUnits":500,"moneyAmount":null,"currencyId":null,"quantity":1,"moneyOwed":0,"appliedPrizeVouchers":[]},"requestShopId":42,"requestTerminalId":null,"requestOperatorId":null,"confirmedByOperatorId":null,"stripePaymentIntentId":null,"stripePaymentStatus":null,"requestedAt":"2026-05-15T10:00:00.000Z","pendingPaymentAt":null,"confirmedAt":null,"fulfillmentStartedAt":null,"collectedAt":null,"cancelledAt":null,"cancelReason":null,"notes":null,"createdAt":"2026-05-15T10:00:00.000Z","updatedAt":"2026-05-15T10:00:00.000Z","variants":[{"typeId":1,"valueId":10,"typeName":"Taglia","value":"M"}],"personalizations":[{"typeId":2,"valueId":20,"typeName":"Nome","value":"Mario"}],"payments":[{"id":1,"walletId":1,"walletUnits":500,"moneyAmount":null,"currencyId":null,"voucherId":null,"walletTransactionId":8001,"movementId":9001,"createdAt":"2026-05-15T10:00:00.000Z"}]}],"totalElements":1,"totalPages":1,"page":1,"size":20}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/prize-requests/{id}":{"get":{"tags":["PrizeRequests"],"description":"Dettaglio di richiesta premio per identificativo.\n\n**Contesto:** Le **Richieste premio** tracciano il ciclo di vita del riscatto: richiesta → conferma → fulfillment → ritiro/consegna → eventuale annullo o rimborso Stripe.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `id` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Dettaglio richiesta premio con varianti, personalizzazioni e pagamenti.\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** PrizeRequests.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Dettaglio richiesta premio con varianti, personalizzazioni e pagamenti.","content":{"application/json":{"schema":{"description":"Dettaglio richiesta premio con varianti, personalizzazioni e pagamenti.","type":"object","additionalProperties":true,"example":{"id":1203,"programId":1,"prizeId":50,"prizeName":"Maglietta","prizeCode":"SKU-001","description":"Maglietta ufficiale in cotone","descriptionLong":"<p>Maglietta ufficiale del programma fedeltà, 100% cotone.</p>","imageUrl":"https://cdn.example/prize-cover.jpg","images":[{"id":1,"url":"https://cdn.example/prize-cover.jpg","sortOrder":0},{"id":2,"url":"https://cdn.example/prize-detail.jpg","sortOrder":1}],"catalogId":10,"catalogName":"Catalogo estivo","customerId":200,"customerCardId":301,"quantity":1,"status":"REQUESTED","fulfillmentStepCode":null,"deliveryMode":"PICKUP_SHOP","pickupShopId":42,"shippingAddress":null,"deliveryAddress":null,"shippingTrackingNumber":null,"fulfillmentStepName":null,"paymentOptionId":100,"paymentSnapshot":{"optionId":100,"walletId":1,"walletUnits":500,"moneyAmount":null,"currencyId":null,"quantity":1,"moneyOwed":0,"appliedPrizeVouchers":[]},"requestShopId":42,"requestTerminalId":null,"requestOperatorId":null,"confirmedByOperatorId":null,"stripePaymentIntentId":null,"stripePaymentStatus":null,"requestedAt":"2026-05-15T10:00:00.000Z","pendingPaymentAt":null,"confirmedAt":null,"fulfillmentStartedAt":null,"collectedAt":null,"cancelledAt":null,"cancelReason":null,"notes":null,"createdAt":"2026-05-15T10:00:00.000Z","updatedAt":"2026-05-15T10:00:00.000Z","variants":[{"typeId":1,"valueId":10,"typeName":"Taglia","value":"M"}],"personalizations":[{"typeId":2,"valueId":20,"typeName":"Nome","value":"Mario"}],"payments":[{"id":1,"walletId":1,"walletUnits":500,"moneyAmount":null,"currencyId":null,"voucherId":null,"walletTransactionId":8001,"movementId":9001,"createdAt":"2026-05-15T10:00:00.000Z"}]}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/prize-requests/{id}/events":{"get":{"tags":["PrizeRequests"],"description":"Elenco paginato di events.\n\n**Contesto:** Le **Richieste premio** tracciano il ciclo di vita del riscatto: richiesta → conferma → fulfillment → ritiro/consegna → eventuale annullo o rimborso Stripe.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `id` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Storico eventi della richiesta premio.\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** PrizeRequests.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Storico eventi della richiesta premio.","content":{"application/json":{"schema":{"description":"Storico eventi della richiesta premio.","type":"object","additionalProperties":true,"example":{"content":[{"id":1,"eventType":"STATUS_CHANGE","fromStatus":"PENDING_PAYMENT","toStatus":"REQUESTED","payload":{},"operatorId":null,"customerId":200,"notes":null,"occurredAt":"2026-05-15T10:00:00.000Z"}]}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/prize-requests/{id}/confirm":{"put":{"tags":["PrizeRequests"],"description":"Aggiorna confirm (campi consentiti).\n\n**Contesto:** Le **Richieste premio** tracciano il ciclo di vita del riscatto: richiesta → conferma → fulfillment → ritiro/consegna → eventuale annullo o rimborso Stripe.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `id` (integer) — obbligatorio\n\n**Body JSON:**\n- `notes` (string) — opzionale\n\n**Risposte di successo:**\n- **200**: Conferma richiesta premio.\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** PrizeRequests.","requestBody":{"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"properties":{"notes":{"type":"string"}}}}}},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Conferma richiesta premio.","content":{"application/json":{"schema":{"description":"Conferma richiesta premio.","type":"object","additionalProperties":true,"example":{"id":1203,"status":"CONFIRMED","supplierQueued":true}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/prize-requests/{id}/fulfillment":{"put":{"tags":["PrizeRequests"],"description":"Aggiorna fulfillment (campi consentiti).\n\n**Contesto:** Le **Richieste premio** tracciano il ciclo di vita del riscatto: richiesta → conferma → fulfillment → ritiro/consegna → eventuale annullo o rimborso Stripe.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `id` (integer) — obbligatorio\n\n**Body JSON:**\n- `stepCode` (string) — opzionale\n- `notes` (string) — opzionale\n\n**Risposte di successo:**\n- **200**: Avvia fulfillment.\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** PrizeRequests.","requestBody":{"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"properties":{"stepCode":{"type":"string"},"notes":{"type":"string"}}}}}},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Avvia fulfillment.","content":{"application/json":{"schema":{"description":"Avvia fulfillment.","type":"object","additionalProperties":true,"example":{"id":1203,"status":"IN_FULFILLMENT","fulfillmentStepCode":"PACKING"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/prize-requests/{id}/fulfillment-step":{"put":{"tags":["PrizeRequests"],"description":"Aggiorna fulfillment-step (campi consentiti).\n\n**Contesto:** Le **Richieste premio** tracciano il ciclo di vita del riscatto: richiesta → conferma → fulfillment → ritiro/consegna → eventuale annullo o rimborso Stripe.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `id` (integer) — obbligatorio\n\n**Body JSON:**\n- `stepCode` (string) — obbligatorio\n- `notes` (string) — opzionale\n\n**Risposte di successo:**\n- **200**: Aggiorna step fulfillment.\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** PrizeRequests.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["stepCode"],"additionalProperties":true,"properties":{"stepCode":{"type":"string"},"notes":{"type":"string"}}}}},"required":true},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Aggiorna step fulfillment.","content":{"application/json":{"schema":{"description":"Aggiorna step fulfillment.","type":"object","additionalProperties":true,"example":{"id":1203,"status":"IN_FULFILLMENT","fulfillmentStepCode":"SHIPPING"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/prize-requests/{id}/home-delivery":{"put":{"tags":["PrizeRequests"],"description":"Update home delivery tracking and/or delivery status (HOME_DELIVERY active statuses).\n\n**Contesto:** Le **Richieste premio** tracciano il ciclo di vita del riscatto: richiesta → conferma → fulfillment → ritiro/consegna → eventuale annullo o rimborso Stripe.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `id` (integer) — obbligatorio\n\n**Body JSON:**\n- `trackingNumber` (string) — opzionale\n- `stepCode` (string) — opzionale\n- `notes` (string) — opzionale\n\n**Risposte di successo:**\n- **200**: Aggiorna tracking e stato consegna a domicilio.\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** PrizeRequests.","requestBody":{"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"properties":{"trackingNumber":{"type":"string","nullable":true},"stepCode":{"type":"string"},"notes":{"type":"string"}}}}}},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Aggiorna tracking e stato consegna a domicilio.","content":{"application/json":{"schema":{"description":"Aggiorna tracking e stato consegna a domicilio.","type":"object","additionalProperties":true,"example":{"id":1203,"status":"OUT_FOR_DELIVERY","deliveryMode":"HOME_DELIVERY","shippingTrackingNumber":"IT123456789","fulfillmentStepCode":null,"fulfillmentStepName":"Nel processo di consegna"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/prize-requests/{id}/confirm-delivery":{"put":{"tags":["PrizeRequests"],"description":"Aggiorna confirm-delivery (campi consentiti).\n\n**Contesto:** Le **Richieste premio** tracciano il ciclo di vita del riscatto: richiesta → conferma → fulfillment → ritiro/consegna → eventuale annullo o rimborso Stripe.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `id` (integer) — obbligatorio\n\n**Body JSON:**\n- `notes` (string) — opzionale\n\n**Risposte di successo:**\n- **200**: Cliente conferma ricezione premio a domicilio.\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** PrizeRequests.","requestBody":{"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"properties":{"notes":{"type":"string"}}}}}},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Cliente conferma ricezione premio a domicilio.","content":{"application/json":{"schema":{"description":"Cliente conferma ricezione premio a domicilio.","type":"object","additionalProperties":true,"example":{"id":1203,"status":"COLLECTED"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/prize-requests/{id}/collect":{"put":{"tags":["PrizeRequests"],"description":"Mark prize as collected/delivered (fulfillment / home delivery / READY_FOR_PICKUP → COLLECTED).\n\n**Contesto:** Le **Richieste premio** tracciano il ciclo di vita del riscatto: richiesta → conferma → fulfillment → ritiro/consegna → eventuale annullo o rimborso Stripe.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `id` (integer) — obbligatorio\n\n**Body JSON:**\n- `notes` (string) — opzionale\n\n**Risposte di successo:**\n- **200**: Segna premio come ritirato.\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** PrizeRequests.","requestBody":{"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"properties":{"notes":{"type":"string"}}}}}},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Segna premio come ritirato.","content":{"application/json":{"schema":{"description":"Segna premio come ritirato.","type":"object","additionalProperties":true,"example":{"id":1203,"status":"COLLECTED","movementId":82951}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/prize-requests/{id}/ready-for-pickup":{"put":{"tags":["PrizeRequests"],"description":"Segna premio pronto per ritiro in negozio.\n\n**Contesto:** Le **Richieste premio** tracciano il ciclo di vita del riscatto: richiesta → conferma → fulfillment → ritiro/consegna → eventuale annullo o rimborso Stripe.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `id` (integer) — obbligatorio\n\n**Body JSON:**\n- `notes` (string) — opzionale\n\n**Risposte di successo:**\n- **200**: Segna premio pronto per ritiro in negozio (IN_FULFILLMENT → READY_FOR_PICKUP).\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** PrizeRequests.","requestBody":{"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"properties":{"notes":{"type":"string"}}}}}},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Segna premio pronto per ritiro in negozio (IN_FULFILLMENT → READY_FOR_PICKUP).","content":{"application/json":{"schema":{"description":"Segna premio pronto per ritiro in negozio (IN_FULFILLMENT → READY_FOR_PICKUP).","type":"object","additionalProperties":true,"example":{"id":1203,"status":"READY_FOR_PICKUP"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/prize-requests/{id}/not-picked-up":{"put":{"tags":["PrizeRequests"],"description":"Segna premio non ritirato dal cliente.\n\n**Contesto:** Le **Richieste premio** tracciano il ciclo di vita del riscatto: richiesta → conferma → fulfillment → ritiro/consegna → eventuale annullo o rimborso Stripe.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `id` (integer) — obbligatorio\n\n**Body JSON:**\n- `notes` (string) — opzionale\n\n**Risposte di successo:**\n- **200**: Segna premio non ritirato (READY_FOR_PICKUP → NOT_PICKED_UP).\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** PrizeRequests.","requestBody":{"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"properties":{"notes":{"type":"string"}}}}}},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Segna premio non ritirato (READY_FOR_PICKUP → NOT_PICKED_UP).","content":{"application/json":{"schema":{"description":"Segna premio non ritirato (READY_FOR_PICKUP → NOT_PICKED_UP).","type":"object","additionalProperties":true,"example":{"id":1203,"status":"NOT_PICKED_UP"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/prize-requests/{id}/returned":{"put":{"tags":["PrizeRequests"],"description":"Segna premio restituito o rimborsato.\n\n**Contesto:** Le **Richieste premio** tracciano il ciclo di vita del riscatto: richiesta → conferma → fulfillment → ritiro/consegna → eventuale annullo o rimborso Stripe.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `id` (integer) — obbligatorio\n\n**Body JSON:**\n- `notes` (string) — opzionale\n- `reason` (string) — opzionale\n\n**Risposte di successo:**\n- **200**: Segna premio restituito/rimborsato (COLLECTED | NOT_PICKED_UP → RETURNED).\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** PrizeRequests.","requestBody":{"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"properties":{"notes":{"type":"string"},"reason":{"type":"string"}}}}}},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Segna premio restituito/rimborsato (COLLECTED | NOT_PICKED_UP → RETURNED).","content":{"application/json":{"schema":{"description":"Segna premio restituito/rimborsato (COLLECTED | NOT_PICKED_UP → RETURNED).","type":"object","additionalProperties":true,"example":{"id":1203,"status":"RETURNED"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/prize-requests/{id}/shipping":{"put":{"tags":["PrizeRequests"],"description":"Update shipping/delivery info. Allowed only while status ∈ {REQUESTED, CONFIRMED, PENDING_PAYMENT}.\n\n**Contesto:** Le **Richieste premio** tracciano il ciclo di vita del riscatto: richiesta → conferma → fulfillment → ritiro/consegna → eventuale annullo o rimborso Stripe.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `id` (integer) — obbligatorio\n\n**Body JSON:**\n- `deliveryMode` (string) — opzionale (valori: PICKUP_SHOP, HOME_DELIVERY)\n- `pickupShopId` (integer) — opzionale\n- `address` (object) — opzionale\n\n**Risposte di successo:**\n- **200**: Aggiorna modalità consegna/spedizione.\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** PrizeRequests.","requestBody":{"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"properties":{"deliveryMode":{"type":"string","enum":["PICKUP_SHOP","HOME_DELIVERY"]},"pickupShopId":{"type":"integer"},"address":{"type":"object","additionalProperties":true,"properties":{"recipientName":{"type":"string"},"phone":{"type":"string"},"street":{"type":"string"},"number":{"type":"string"},"floor":{"type":"string"},"apartment":{"type":"string"},"zipCode":{"type":"string"},"city":{"type":"string"},"state":{"type":"string"},"country":{"type":"string"},"notes":{"type":"string","description":"Note di consegna (istruzioni per il corriere, orari, ecc.)"}}}}}}}},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Aggiorna modalità consegna/spedizione.","content":{"application/json":{"schema":{"description":"Aggiorna modalità consegna/spedizione.","type":"object","additionalProperties":true,"example":{"id":1203,"deliveryMode":"HOME_DELIVERY","pickupShopId":null,"shippingAddress":{"street":"Via Roma","number":"1","city":"Firenze","zipCode":"50100","country":"IT","notes":"Consegnare dalle 9 alle 12"}}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/prize-requests/{id}/cancel":{"post":{"tags":["PrizeRequests"],"description":"Cancel a prize request (terminal). Wallets and Stripe are auto-refunded. Backoffice requires reason.\n\n**Contesto:** Le **Richieste premio** tracciano il ciclo di vita del riscatto: richiesta → conferma → fulfillment → ritiro/consegna → eventuale annullo o rimborso Stripe.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `id` (integer) — obbligatorio\n\n**Body JSON:**\n- `reason` (string) — opzionale\n\n**Risposte di successo:**\n- **200**: Annulla richiesta premio con eventuali rimborsi.\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** PrizeRequests.","requestBody":{"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"properties":{"reason":{"type":"string","minLength":1}}}}}},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Annulla richiesta premio con eventuali rimborsi.","content":{"application/json":{"schema":{"description":"Annulla richiesta premio con eventuali rimborsi.","type":"object","additionalProperties":true,"example":{"id":1203,"status":"CANCELLED","cancelledDispatches":1,"walletRefunds":[{"originalId":1,"refundId":2,"walletId":1,"amount":500,"newStatus":"REFUNDED"}],"stripeRefund":{"action":"refunded","id":"pi_3Pabc123","amount":1998}}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/prize-confirmation-blacklist":{"get":{"tags":["PrizeConfirmationBlacklist"],"description":"Elenco paginato di prize-confirmation-blacklist del programma indicato in `programId`.\n\n**Contesto:** Un **Programma** è l'istanza isolata del loyalty (ex Campaign): contiene negozi, clienti, wallet, promo, premi e tutte le configurazioni. Livello SYSTEM per la gestione piattaforma.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n\n**Querystring:**\n- `search` (string) — opzionale: Filter by customer first/last name or email (ILIKE).\n- `page` (integer) — opzionale [default: 1]\n- `size` (integer) — opzionale [default: 50]\n\n**Risposte di successo:**\n- **200**: Lista prize-confirmation-blacklist\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** PrizeConfirmationBlacklist.","parameters":[{"schema":{"type":"string"},"in":"query","name":"search","required":false,"description":"Filter by customer first/last name or email (ILIKE)."},{"schema":{"type":"integer","default":1},"in":"query","name":"page","required":false},{"schema":{"type":"integer","default":50},"in":"query","name":"size","required":false},{"schema":{"type":"integer"},"in":"path","name":"programId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Lista prize-confirmation-blacklist","content":{"application/json":{"schema":{"description":"Lista prize-confirmation-blacklist","type":"object","additionalProperties":true,"example":{"content":[{"id":1,"name":"Esempio","programId":1,"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}],"totalElements":1,"totalPages":1,"page":1,"size":20}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"post":{"tags":["PrizeConfirmationBlacklist"],"description":"Add one or more customers to the confirmation blacklist. Pass `customerId` for single or `customerIds` for bulk.\n\n**Contesto:** Un **Programma** è l'istanza isolata del loyalty (ex Campaign): contiene negozi, clienti, wallet, promo, premi e tutte le configurazioni. Livello SYSTEM per la gestione piattaforma.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n\n**Body JSON:**\n- `customerId` (integer) — opzionale\n- `customerIds` (array) — opzionale\n- `notes` (string) — opzionale\n\n**Risposte di successo:**\n- **201**: Risorsa creata (prize-confirmation-blacklist)\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** PrizeConfirmationBlacklist.","requestBody":{"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"properties":{"customerId":{"type":"integer"},"customerIds":{"type":"array","items":{"type":"integer"}},"notes":{"type":"string"}}}}}},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"201":{"description":"Risorsa creata (prize-confirmation-blacklist)","content":{"application/json":{"schema":{"description":"Risorsa creata (prize-confirmation-blacklist)","type":"object","additionalProperties":true,"example":{"id":1,"name":"Esempio","programId":1,"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"delete":{"tags":["PrizeConfirmationBlacklist"],"description":"Elimina una programma fedeltà del programma indicato in `programId` (soft delete o `TERMINATED` se vincoli FK).\n\n**Contesto:** Un **Programma** è l'istanza isolata del loyalty (ex Campaign): contiene negozi, clienti, wallet, promo, premi e tutte le configurazioni. Livello SYSTEM per la gestione piattaforma.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n\n**Body JSON:**\n- `customerIds` (array) — obbligatorio\n\n**Risposte di successo:**\n- **204**: Operazione riuscita senza corpo di risposta.\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** PrizeConfirmationBlacklist.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["customerIds"],"additionalProperties":true,"properties":{"customerIds":{"type":"array","items":{"type":"integer"},"minItems":1}}}}},"required":true},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"204":{"description":"Operazione riuscita senza corpo di risposta."},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/prize-confirmation-blacklist/count":{"get":{"tags":["PrizeConfirmationBlacklist"],"description":"Elenco paginato di count del programma indicato in `programId`.\n\n**Contesto:** Un **Programma** è l'istanza isolata del loyalty (ex Campaign): contiene negozi, clienti, wallet, promo, premi e tutte le configurazioni. Livello SYSTEM per la gestione piattaforma.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Number of customers in the prize confirmation blacklist for a program\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** PrizeConfirmationBlacklist.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Number of customers in the prize confirmation blacklist for a program","content":{"application/json":{"schema":{"type":"object","description":"Number of customers in the prize confirmation blacklist for a program","properties":{"total":{"type":"integer","example":12}},"example":{"total":12}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/prize-confirmation-blacklist/{customerId}":{"get":{"tags":["PrizeConfirmationBlacklist"],"description":"Dettaglio di una programma fedeltà per identificativo del programma indicato in `programId`.\n\n**Contesto:** Un **Programma** è l'istanza isolata del loyalty (ex Campaign): contiene negozi, clienti, wallet, promo, premi e tutte le configurazioni. Livello SYSTEM per la gestione piattaforma.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n- `customerId` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Dettaglio prize-confirmation-blacklist\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** PrizeConfirmationBlacklist.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"customerId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Dettaglio prize-confirmation-blacklist","content":{"application/json":{"schema":{"description":"Dettaglio prize-confirmation-blacklist","type":"object","additionalProperties":true,"example":{"id":1,"name":"Esempio","programId":1,"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"delete":{"tags":["PrizeConfirmationBlacklist"],"description":"Elimina una programma fedeltà del programma indicato in `programId` (soft delete o `TERMINATED` se vincoli FK).\n\n**Contesto:** Un **Programma** è l'istanza isolata del loyalty (ex Campaign): contiene negozi, clienti, wallet, promo, premi e tutte le configurazioni. Livello SYSTEM per la gestione piattaforma.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n- `customerId` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **204**: Operazione riuscita senza corpo di risposta.\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** PrizeConfirmationBlacklist.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"customerId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"204":{"description":"Operazione riuscita senza corpo di risposta."},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/prize-supplier-dispatches":{"get":{"tags":["PrizeSupplierDispatches"],"description":"Elenco paginato di prize-supplier-dispatches del programma indicato in `programId`.\n\n**Contesto:** Un **Programma** è l'istanza isolata del loyalty (ex Campaign): contiene negozi, clienti, wallet, promo, premi e tutte le configurazioni. Livello SYSTEM per la gestione piattaforma.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n\n**Querystring:**\n- `status` (string) — opzionale (valori: QUEUED, IN_PROGRESS, SUCCESS, FAILED_TRANSIENT, FAILED_PERMANENT, CANCELLED)\n- `supplierId` (integer) — opzionale\n- `requestId` (integer) — opzionale\n- `ready` (boolean) — opzionale: If true, only QUEUED/FAILED_TRANSIENT with scheduled_at <= now.\n- `page` (integer) — opzionale [default: 1]\n- `size` (integer) — opzionale [default: 20]\n\n**Risposte di successo:**\n- **200**: Lista prize-supplier-dispatches\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** PrizeSupplierDispatches.","parameters":[{"schema":{"type":"string","enum":["QUEUED","IN_PROGRESS","SUCCESS","FAILED_TRANSIENT","FAILED_PERMANENT","CANCELLED"]},"in":"query","name":"status","required":false},{"schema":{"type":"integer"},"in":"query","name":"supplierId","required":false},{"schema":{"type":"integer"},"in":"query","name":"requestId","required":false},{"schema":{"type":"boolean"},"in":"query","name":"ready","required":false,"description":"If true, only QUEUED/FAILED_TRANSIENT with scheduled_at <= now."},{"schema":{"type":"integer","default":1},"in":"query","name":"page","required":false},{"schema":{"type":"integer","default":20},"in":"query","name":"size","required":false},{"schema":{"type":"integer"},"in":"path","name":"programId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Lista prize-supplier-dispatches","content":{"application/json":{"schema":{"description":"Lista prize-supplier-dispatches","type":"object","additionalProperties":true,"example":{"content":[{"id":1,"name":"Esempio","programId":1,"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}],"totalElements":1,"totalPages":1,"page":1,"size":20}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/prize-supplier-dispatches/{id}":{"get":{"tags":["PrizeSupplierDispatches"],"description":"Dettaglio di prize-supplier-dispatches per identificativo.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `id` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Supplier dispatch detail\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** PrizeSupplierDispatches.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Supplier dispatch detail","content":{"application/json":{"schema":{"type":"object","description":"Supplier dispatch detail","properties":{"id":{"type":"integer","example":501},"requestId":{"type":"integer","example":1203},"supplierId":{"type":"integer","example":7},"supplierName":{"type":"string","example":"Fornitore Demo"},"programId":{"type":"integer","example":1},"status":{"type":"string","example":"SUCCESS"},"attemptCount":{"type":"integer","example":1},"maxAttempts":{"type":"integer","example":5},"scheduledAt":{"type":"string","format":"date-time"},"lastAttemptAt":{"type":"string","format":"date-time"},"completedAt":{"type":"string","format":"date-time"},"lastStatusCode":{"type":"integer","example":200},"lastResponseBody":{"type":"object","additionalProperties":true},"lastError":{"type":"string","nullable":true},"externalReference":{"type":"string","example":"EXT-REF-99","nullable":true},"payload":{"type":"object","additionalProperties":true},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"example":{"id":501,"requestId":1203,"supplierId":7,"supplierName":"Fornitore Demo","programId":1,"status":"SUCCESS","attemptCount":1,"maxAttempts":5,"scheduledAt":"2026-05-19T08:00:00.000Z","lastAttemptAt":"2026-05-19T08:00:05.000Z","completedAt":"2026-05-19T08:00:05.000Z","lastStatusCode":200,"lastResponseBody":{"ok":true},"lastError":null,"externalReference":"EXT-REF-99","payload":{"prizeRequestId":1203},"createdAt":"2026-05-19T08:00:00.000Z","updatedAt":"2026-05-19T08:00:05.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/prize-requests/{requestId}/dispatches":{"get":{"tags":["PrizeSupplierDispatches"],"description":"Elenco paginato di dispatches.\n\n**Contesto:** Le **Richieste premio** tracciano il ciclo di vita del riscatto: richiesta → conferma → fulfillment → ritiro/consegna → eventuale annullo o rimborso Stripe.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `requestId` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Dispatches linked to a prize request\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** PrizeSupplierDispatches.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"requestId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Dispatches linked to a prize request","content":{"application/json":{"schema":{"type":"object","description":"Dispatches linked to a prize request","properties":{"content":{"type":"array","items":{"type":"object","additionalProperties":true},"example":[{"id":501,"requestId":1203,"supplierId":7,"supplierName":"Fornitore Demo","programId":1,"status":"QUEUED","attemptCount":0,"maxAttempts":5,"scheduledAt":"2026-05-19T09:00:00.000Z","lastAttemptAt":null,"completedAt":null,"lastStatusCode":null,"lastResponseBody":null,"lastError":null,"externalReference":null,"payload":{"prizeRequestId":1203},"createdAt":"2026-05-19T09:00:00.000Z","updatedAt":"2026-05-19T09:00:00.000Z"}]}},"example":{"content":[{"id":501,"requestId":1203,"supplierId":7,"supplierName":"Fornitore Demo","programId":1,"status":"QUEUED","attemptCount":0,"maxAttempts":5,"scheduledAt":"2026-05-19T09:00:00.000Z","lastAttemptAt":null,"completedAt":null,"lastStatusCode":null,"lastResponseBody":null,"lastError":null,"externalReference":null,"payload":{"prizeRequestId":1203},"createdAt":"2026-05-19T09:00:00.000Z","updatedAt":"2026-05-19T09:00:00.000Z"}]}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/prize-supplier-dispatches/{id}/retry":{"post":{"tags":["PrizeSupplierDispatches"],"description":"Force a manual retry of a dispatch (resets scheduled_at; resume=true esegue il tentativo subito).\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `id` (integer) — obbligatorio\n\n**Body JSON:**\n- `resumeNow` (boolean) — opzionale: Se false, lascia in coda per il prossimo tick del worker. [default: true]\n\n**Risposte di successo:**\n- **200**: Ritenta invio al fornitore.\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** PrizeSupplierDispatches.","requestBody":{"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"properties":{"resumeNow":{"type":"boolean","default":true,"description":"Se false, lascia in coda per il prossimo tick del worker."}}}}}},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Ritenta invio al fornitore.","content":{"application/json":{"schema":{"description":"Ritenta invio al fornitore.","type":"object","additionalProperties":true,"example":{"id":501,"status":"SUCCESS","statusCode":200,"externalReference":"EXT-REF-99"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/prize-supplier-dispatches/{id}/cancel":{"post":{"tags":["PrizeSupplierDispatches"],"description":"Crea cancel.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `id` (integer) — obbligatorio\n\n**Body JSON:**\n- `reason` (string) — opzionale\n\n**Risposte di successo:**\n- **200**: Annulla dispatch fornitore.\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** PrizeSupplierDispatches.","requestBody":{"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"properties":{"reason":{"type":"string"}}}}}},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Annulla dispatch fornitore.","content":{"application/json":{"schema":{"description":"Annulla dispatch fornitore.","type":"object","additionalProperties":true,"example":{"id":501,"status":"CANCELLED"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/prize-supplier-dispatches/process-queue":{"post":{"tags":["PrizeSupplierDispatches"],"description":"Crea process-queue.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Body JSON:**\n- `batchSize` (integer) — opzionale [default: 10]\n\n**Risposte di successo:**\n- **200**: Elabora coda dispatch fornitore.\n\n**Errori documentati:** 400, 401, 403, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** PrizeSupplierDispatches.","requestBody":{"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"properties":{"batchSize":{"type":"integer","minimum":1,"maximum":200,"default":10}}}}}},"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Elabora coda dispatch fornitore.","content":{"application/json":{"schema":{"description":"Elabora coda dispatch fornitore.","type":"object","additionalProperties":true,"example":{"processed":2,"results":[{"id":501,"status":"SUCCESS","statusCode":200,"externalReference":"EXT-REF-99"},{"id":502,"status":"ERROR","error":"timeout"}]}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/stripe-config":{"get":{"summary":"Read Stripe config for a program (secrets masked)","tags":["Prize Stripe"],"description":"Elenco paginato di stripe-config del programma indicato in `programId`.\n\n**Contesto:** Integrazione **Stripe** per pagamenti online (premi, coupon, gift card): config chiavi dual-mode (test/live), PaymentIntent, webhook ed eventi.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Configurazione Stripe del programma (chiavi mascherate).\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Prize Stripe.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Configurazione Stripe del programma (chiavi mascherate).","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"description":"Configurazione Stripe del programma (chiavi mascherate).","example":{"programId":1,"enabled":true,"livemode":false,"publishableKey":"pk_test_xxx","defaultCurrency":"eur","apiVersion":"2024-06-20","notes":null,"secretKey":"***","webhookSecret":"***","hasSecretKey":true,"hasWebhookSecret":true,"createdAt":"2026-05-15T10:00:00.000Z","updatedAt":"2026-05-15T10:00:00.000Z","createdByOperatorId":1,"updatedByOperatorId":1}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"put":{"summary":"Upsert Stripe config for a program","tags":["Prize Stripe"],"description":"Aggiorna una programma fedeltà (campi consentiti) del programma indicato in `programId`.\n\n**Contesto:** Integrazione **Stripe** per pagamenti online (premi, coupon, gift card): config chiavi dual-mode (test/live), PaymentIntent, webhook ed eventi.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n\n**Body JSON:**\n- `enabled` (boolean) — opzionale\n- `livemode` (boolean) — opzionale: false = usa chiavi TEST/sandbox; true = usa chiavi LIVE\n- `secretKey` (string) — opzionale: Legacy: applicato al modo attivo (livemode)\n- `publishableKey` (string) — opzionale: Legacy: applicato al modo attivo (livemode)\n- `webhookSecret` (string) — opzionale: Legacy: applicato al modo attivo (livemode)\n- `testSecretKey` (string) — opzionale\n- `testPublishableKey` (string) — opzionale\n- `testWebhookSecret` (string) — opzionale\n- `liveSecretKey` (string) — opzionale\n- `livePublishableKey` (string) — opzionale\n- `liveWebhookSecret` (string) — opzionale\n- `defaultCurrency` (string) — opzionale\n- `apiVersion` (string) — opzionale\n- `notes` (string) — opzionale\n\n**Risposte di successo:**\n- **200**: Aggiorna configurazione Stripe del programma.\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Prize Stripe.","requestBody":{"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"properties":{"enabled":{"type":"boolean"},"livemode":{"type":"boolean","description":"false = usa chiavi TEST/sandbox; true = usa chiavi LIVE"},"secretKey":{"type":"string","description":"Legacy: applicato al modo attivo (livemode)","nullable":true},"publishableKey":{"type":"string","description":"Legacy: applicato al modo attivo (livemode)","nullable":true},"webhookSecret":{"type":"string","description":"Legacy: applicato al modo attivo (livemode)","nullable":true},"testSecretKey":{"type":"string","nullable":true},"testPublishableKey":{"type":"string","nullable":true},"testWebhookSecret":{"type":"string","nullable":true},"liveSecretKey":{"type":"string","nullable":true},"livePublishableKey":{"type":"string","nullable":true},"liveWebhookSecret":{"type":"string","nullable":true},"defaultCurrency":{"type":"string","minLength":3,"maxLength":3},"apiVersion":{"type":"string","nullable":true},"notes":{"type":"string","nullable":true}}}}}},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Aggiorna configurazione Stripe del programma.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"description":"Aggiorna configurazione Stripe del programma.","example":{"programId":1,"enabled":true,"livemode":false,"publishableKey":"pk_test_xxx","defaultCurrency":"eur","hasSecretKey":true,"hasWebhookSecret":true,"updatedAt":"2026-05-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"delete":{"summary":"Delete Stripe config for a program","tags":["Prize Stripe"],"description":"Elimina una programma fedeltà del programma indicato in `programId` (soft delete o `TERMINATED` se vincoli FK).\n\n**Contesto:** Integrazione **Stripe** per pagamenti online (premi, coupon, gift card): config chiavi dual-mode (test/live), PaymentIntent, webhook ed eventi.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Elimina configurazione Stripe del programma.\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Prize Stripe.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Elimina configurazione Stripe del programma.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"description":"Elimina configurazione Stripe del programma.","example":{"deleted":1}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/prize-requests/{id}/payment-intent":{"post":{"summary":"Create or get Stripe PaymentIntent for a prize request","tags":["Prize Stripe"],"description":"Crea PaymentIntent Stripe per pagamento premio.\n\n**Contesto:** Le **Richieste premio** tracciano il ciclo di vita del riscatto: richiesta → conferma → fulfillment → ritiro/consegna → eventuale annullo o rimborso Stripe.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `id` (integer) — obbligatorio\n\n**Body JSON:**\n- `returnUrl` (string) — opzionale\n\n**Risposte di successo:**\n- **200**: Crea o riusa PaymentIntent Stripe per richiesta premio.\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Prize Stripe.","requestBody":{"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"properties":{"returnUrl":{"type":"string"}}}}}},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Crea o riusa PaymentIntent Stripe per richiesta premio.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"description":"Crea o riusa PaymentIntent Stripe per richiesta premio.","example":{"paymentIntentId":"pi_3Pabc123","clientSecret":"pi_3Pabc123_secret_xyz","publishableKey":"pk_test_xxx","amount":1998,"currency":"eur","status":"requires_payment_method","reused":false}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/webhooks/stripe":{"post":{"summary":"Stripe webhook receiver per-program (signature-verified)","tags":["Prize Stripe"],"description":"Crea stripe del programma indicato in `programId`.\n\n**Contesto:** I **Webhook** notificano sistemi esterni su eventi (movimento, promo, cambio tier). Configurabili con URL, trigger e payload template.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Webhook Stripe (premi, coupon, gift card).\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Prize Stripe.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true}],"responses":{"200":{"description":"Webhook Stripe (premi, coupon, gift card).","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"description":"Webhook Stripe (premi, coupon, gift card).","example":{"ok":true,"eventType":"payment_intent.succeeded","action":"finalized","requestId":1203}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/stripe-webhook-events":{"get":{"summary":"List Stripe webhook events received (audit)","tags":["Prize Stripe"],"description":"Elenco paginato di stripe-webhook-events del programma indicato in `programId`.\n\n**Contesto:** Integrazione **Stripe** per pagamenti online (premi, coupon, gift card): config chiavi dual-mode (test/live), PaymentIntent, webhook ed eventi.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n\n**Querystring:**\n- `page` (integer) — opzionale [default: 1]\n- `size` (integer) — opzionale [default: 20]\n- `eventType` (string) — opzionale\n- `status` (string) — opzionale (valori: RECEIVED, PROCESSED, SKIPPED, FAILED)\n- `requestId` (integer) — opzionale\n- `paymentIntentId` (string) — opzionale\n\n**Risposte di successo:**\n- **200**: Log eventi webhook Stripe del programma.\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Prize Stripe.","parameters":[{"schema":{"type":"integer","minimum":1,"default":1},"in":"query","name":"page","required":false},{"schema":{"type":"integer","minimum":1,"maximum":200,"default":20},"in":"query","name":"size","required":false},{"schema":{"type":"string"},"in":"query","name":"eventType","required":false},{"schema":{"type":"string","enum":["RECEIVED","PROCESSED","SKIPPED","FAILED"]},"in":"query","name":"status","required":false},{"schema":{"type":"integer"},"in":"query","name":"requestId","required":false},{"schema":{"type":"string"},"in":"query","name":"paymentIntentId","required":false},{"schema":{"type":"integer"},"in":"path","name":"programId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Log eventi webhook Stripe del programma.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"description":"Log eventi webhook Stripe del programma.","example":{"page":1,"size":20,"total":1,"pages":1,"items":[{"id":55,"eventId":"evt_1abc","eventType":"payment_intent.succeeded","processingStatus":"PROCESSED","processingError":null,"paymentIntentId":"pi_3Pabc123","prizeRequestId":1203,"programId":1,"receivedAt":"2026-05-15T10:00:00.000Z","processedAt":"2026-05-15T10:00:00.000Z","livemode":false}]}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/prize-requests/{id}/stripe-refund":{"post":{"summary":"Manually refund/cancel Stripe PaymentIntent for a prize request","tags":["Prize Stripe"],"description":"Emette rimborso Stripe su richiesta premio pagata online.\n\n**Contesto:** Le **Richieste premio** tracciano il ciclo di vita del riscatto: richiesta → conferma → fulfillment → ritiro/consegna → eventuale annullo o rimborso Stripe.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `id` (integer) — obbligatorio\n\n**Body JSON:**\n- `reason` (string) — opzionale\n\n**Risposte di successo:**\n- **200**: Rimborsa o annulla PaymentIntent Stripe.\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Prize Stripe.","requestBody":{"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"properties":{"reason":{"type":"string"}}}}}},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Rimborsa o annulla PaymentIntent Stripe.","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"description":"Rimborsa o annulla PaymentIntent Stripe.","example":{"action":"refunded","id":"pi_3Pabc123","amount":1998}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/customers/{customerId}/prize-wishlist":{"get":{"summary":"List prize IDs in customer wishlist for a program","tags":["PrizeWishlist"],"description":"Elenco paginato di prize-wishlist.\n\n**Contesto:** La **Wishlist premi** è la lista dei premi preferiti del cliente, usata in APP per segnalare interesse prima del riscatto.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `customerId` (integer) — obbligatorio\n\n**Querystring:**\n- `programId` (integer) — obbligatorio\n- `page` (integer) — opzionale [default: 1]\n- `size` (integer) — opzionale [default: 20]\n\n**Risposte di successo:**\n- **200**: Paginated customer prize wishlist\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** PrizeWishlist.","parameters":[{"schema":{"type":"integer"},"in":"query","name":"programId","required":true},{"schema":{"type":"integer","minimum":1,"default":1},"in":"query","name":"page","required":false},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":20},"in":"query","name":"size","required":false},{"schema":{"type":"integer"},"in":"path","name":"customerId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Paginated customer prize wishlist","content":{"application/json":{"schema":{"type":"object","description":"Paginated customer prize wishlist","properties":{"page":{"type":"integer","example":1},"size":{"type":"integer","example":20},"totalElements":{"type":"integer","example":2},"totalPages":{"type":"integer","example":1},"items":{"type":"array","items":{"type":"object","properties":{"prizeId":{"type":"integer","example":42},"prizeName":{"type":"string","example":"Buono regalo 50€"},"prizeCode":{"type":"string","example":"PRZ-0042"},"imageUrl":{"type":"string","example":"https://cdn.example/prize-cover.jpg","nullable":true},"images":{"type":"array","items":{"type":"object","properties":{"id":{"type":"integer","example":1},"url":{"type":"string","example":"https://cdn.example/prize-cover.jpg"},"sortOrder":{"type":"integer","example":0}}},"example":[{"id":1,"url":"https://cdn.example/prize-cover.jpg","sortOrder":0}]},"createdAt":{"type":"string","format":"date-time","example":"2026-05-19T10:30:00.000Z"}}},"example":[{"prizeId":42,"prizeName":"Buono regalo 50€","prizeCode":"PRZ-0042","imageUrl":"https://cdn.example/prize-cover.jpg","images":[{"id":1,"url":"https://cdn.example/prize-cover.jpg","sortOrder":0}],"createdAt":"2026-05-19T10:30:00.000Z"}]}},"example":{"page":1,"size":20,"totalElements":1,"totalPages":1,"items":[{"prizeId":42,"prizeName":"Buono regalo 50€","prizeCode":"PRZ-0042","imageUrl":"https://cdn.example/prize-cover.jpg","images":[{"id":1,"url":"https://cdn.example/prize-cover.jpg","sortOrder":0}],"createdAt":"2026-05-19T10:30:00.000Z"}]}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"post":{"summary":"Add a prize to customer wishlist","tags":["PrizeWishlist"],"description":"Crea prize-wishlist.\n\n**Contesto:** La **Wishlist premi** è la lista dei premi preferiti del cliente, usata in APP per segnalare interesse prima del riscatto.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `customerId` (integer) — obbligatorio\n\n**Body JSON:**\n- `programId` (integer) — obbligatorio\n- `prizeId` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **201**: Default Response\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** PrizeWishlist.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["programId","prizeId"],"properties":{"programId":{"type":"integer"},"prizeId":{"type":"integer"}}}}},"required":true},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"customerId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"201":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"prizeId":{"type":"integer","example":42},"programId":{"type":"integer","example":1},"customerId":{"type":"integer","example":1001}},"example":{"prizeId":42,"programId":1,"customerId":1001}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/customers/{customerId}/prize-wishlist/{prizeId}":{"delete":{"summary":"Remove a prize from customer wishlist","tags":["PrizeWishlist"],"description":"Elimina prize-wishlist (soft delete o `TERMINATED` se vincoli FK).\n\n**Contesto:** La **Wishlist premi** è la lista dei premi preferiti del cliente, usata in APP per segnalare interesse prima del riscatto.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `customerId` (integer) — obbligatorio\n- `prizeId` (integer) — obbligatorio\n\n**Querystring:**\n- `programId` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Default Response\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** PrizeWishlist.","parameters":[{"schema":{"type":"integer"},"in":"query","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"customerId","required":true},{"schema":{"type":"integer"},"in":"path","name":"prizeId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"removed":{"type":"boolean","example":true},"prizeId":{"type":"integer","example":42}},"example":{"removed":true,"prizeId":42}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/prize-interactions":{"post":{"summary":"Record one or more prize engagement events","tags":["PrizeEngagement"],"description":"Crea prize-interactions del programma indicato in `programId`.\n\n**Contesto:** L'**Engagement premi** registra visualizzazioni, interazioni e suggerimenti personalizzati per aumentare conversione al riscatto.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n\n**Body JSON:**\n- `customerId` (integer) — opzionale\n- `events` (array) — obbligatorio\n\n**Risposte di successo:**\n- **201**: Risorsa creata (prize-interactions)\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** PrizeEngagement.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["events"],"properties":{"customerId":{"type":"integer","nullable":true},"events":{"type":"array","minItems":1,"maxItems":100,"items":{"type":"object","required":["type"],"properties":{"type":{"type":"string","enum":["CATALOG_OPENED","PRIZE_VIEWED","PRIZE_IMAGES_OPENED","PRIZE_WISHLIST_ADDED","PRIZE_WISHLIST_REMOVED"]},"prizeId":{"type":"integer","nullable":true},"catalogId":{"type":"integer","nullable":true},"metadata":{"type":"object","additionalProperties":true}}}}}}}},"required":true},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"201":{"description":"Risorsa creata (prize-interactions)","content":{"application/json":{"schema":{"type":"object","properties":{"inserted":{"type":"integer","example":3}},"example":{"inserted":3},"description":"Risorsa creata (prize-interactions)","additionalProperties":true}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/vouchers":{"post":{"tags":["Vouchers"],"description":"Crea voucher.\n\n**Contesto:** I **Voucher/Coupon** sono codici sconto o benefit monouso. Si creano singolarmente, in lotti (set) o via promo; hanno validità, stato e tracciamento utilizzo.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Body JSON:**\n- `programId` (integer) — obbligatorio\n- `customerId` (integer) — obbligatorio\n- `title` (string) — opzionale\n- `description` (string) — opzionale\n- `valueType` (string) — obbligatorio (valori: FIXED, PERCENTAGE)\n- `discountValue` (number) — opzionale\n- `discountPercent` (number) — opzionale\n- `currencyId` (integer) — opzionale\n- `expiresAt` (string) — opzionale\n- `minimumAmount` (number) — opzionale\n- `allowedShopIds` (array) — opzionale\n- `allowedShopCategoryIds` (array) — opzionale\n\n**Risposte di successo:**\n- **201**: Voucher creato / assegnato\n\n**Errori documentati:** 400, 401, 403, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Vouchers.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["programId","customerId","valueType"],"properties":{"programId":{"type":"integer"},"customerId":{"type":"integer"},"title":{"type":"string"},"description":{"type":"string"},"valueType":{"type":"string","enum":["FIXED","PERCENTAGE"]},"discountValue":{"type":"number"},"discountPercent":{"type":"number"},"currencyId":{"type":"integer"},"expiresAt":{"type":"string","format":"date-time"},"minimumAmount":{"type":"number"},"allowedShopIds":{"type":"array","items":{"type":"integer"}},"allowedShopCategoryIds":{"type":"array","items":{"type":"integer"}}}}}},"required":true},"security":[{"bearerAuth":[]}],"responses":{"201":{"description":"Voucher creato / assegnato","content":{"application/json":{"schema":{"description":"Voucher creato / assegnato","type":"object","additionalProperties":true,"example":{"id":1,"code":"ABC123","programId":1,"status":"DELIVERED","title":"Voucher","valueType":"FIXED","discountValue":10,"createdAt":"2026-01-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/vouchers/setGen":{"post":{"tags":["Vouchers"],"description":"Genera un lotto di voucher: quantity (N codici senza cliente) oppure audienceId (un voucher per cliente). Mutuamente esclusivi. deferGeneration=true registra solo il lotto; emetti i coupon con POST /vouchers/sets/:id/generate.\n\n**Contesto:** I **Voucher/Coupon** sono codici sconto o benefit monouso. Si creano singolarmente, in lotti (set) o via promo; hanno validità, stato e tracciamento utilizzo.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Body JSON:**\n- `programId` (integer) — obbligatorio\n- `title` (string) — opzionale\n- `description` (string) — opzionale\n- `valueType` (string) — obbligatorio (valori: FIXED, PERCENTAGE)\n- `discountValue` (number) — opzionale\n- `discountPercent` (number) — opzionale\n- `currencyId` (integer) — opzionale\n- `expiresAt` (string) — opzionale\n- `minimumAmount` (number) — opzionale\n- `allowedShopIds` (array) — opzionale\n- `allowedShopCategoryIds` (array) — opzionale\n- `quantity` (integer) — opzionale\n- `audienceId` (integer) — opzionale\n- `promoExpiryDays` (integer) — opzionale: Giorni validità per emissioni da azione promo COUPON (non per i voucher creati in questo batch)\n- `deferGeneration` (boolean) — opzionale: Se true: salva solo il lotto, senza creare righe vouchers_delivered\n- `extraData` (string) — opzionale: Parametri aggiuntivi (es. JSON), max 2000 caratteri\n- `imageUrl` (string) — opzionale: URL immagine lotto (es. S3), max 2000 caratteri\n\n**Risposte di successo:**\n- **201**: Lotto creato\n\n**Errori documentati:** 400, 401, 403, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Vouchers.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["programId","valueType"],"properties":{"programId":{"type":"integer"},"title":{"type":"string"},"description":{"type":"string"},"valueType":{"type":"string","enum":["FIXED","PERCENTAGE"]},"discountValue":{"type":"number"},"discountPercent":{"type":"number"},"currencyId":{"type":"integer"},"expiresAt":{"type":"string","format":"date-time"},"minimumAmount":{"type":"number"},"allowedShopIds":{"type":"array","items":{"type":"integer"}},"allowedShopCategoryIds":{"type":"array","items":{"type":"integer"}},"productRestricted":{"type":"boolean"},"productIds":{"type":"array","items":{"type":"integer"}},"productCategoryIds":{"type":"array","items":{"type":"integer"}},"productApplyMode":{"type":"string","enum":["ONE_UNIT","ALL_UNITS","MAX_UNITS",null],"nullable":true},"productMaxUnits":{"type":"number","nullable":true},"quantity":{"type":"integer"},"audienceId":{"type":"integer"},"promoExpiryDays":{"type":"integer","description":"Giorni validità per emissioni da azione promo COUPON se promoExpiryMode=DAYS (non per i voucher creati in questo batch)","nullable":true},"promoExpiryMode":{"type":"string","enum":["DAYS","END_OF_MONTH"],"description":"DAYS: N giorni da emissione; END_OF_MONTH: ultimo giorno del mese di generazione"},"deferGeneration":{"type":"boolean","description":"Se true: salva solo il lotto, senza creare righe vouchers_delivered"},"extraData":{"type":"string","description":"Parametri aggiuntivi (es. JSON), max 2000 caratteri"},"imageUrl":{"type":"string","description":"URL immagine lotto (es. S3), max 2000 caratteri","nullable":true}}}}},"required":true},"security":[{"bearerAuth":[]}],"responses":{"201":{"description":"Lotto creato","content":{"application/json":{"schema":{"description":"Lotto creato","type":"object","additionalProperties":true,"example":{"id":1,"programId":1,"setCode":"LOTTO1","title":"Lotto","valueType":"FIXED","imageUrl":"https://cdn.example/voucher-lotto.png","createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/vouchers/sets/{id}/generate":{"post":{"tags":["Vouchers"],"description":"Emette i coupon per un lotto creato con deferGeneration=true (vouchers_generated=false). Idempotente: errore se già generati.\n\n**Contesto:** I **Voucher/Coupon** sono codici sconto o benefit monouso. Si creano singolarmente, in lotti (set) o via promo; hanno validità, stato e tracciamento utilizzo.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `id` (integer) — obbligatorio\n\n**Body JSON:**\n- `programId` (integer) — opzionale: Opzionale: verifica che il lotto appartenga al programma\n\n**Risposte di successo:**\n- **200**: Generazione voucher\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Vouchers.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"programId":{"type":"integer","description":"Opzionale: verifica che il lotto appartenga al programma"}}}}}},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Generazione voucher","content":{"application/json":{"schema":{"description":"Generazione voucher","type":"object","additionalProperties":true,"example":{"generated":100,"setId":1}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/vouchers/dashboard":{"get":{"tags":["Vouchers"],"description":"Elenco paginato di dashboard.\n\n**Contesto:** I **Voucher/Coupon** sono codici sconto o benefit monouso. Si creano singolarmente, in lotti (set) o via promo; hanno validità, stato e tracciamento utilizzo.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Querystring:**\n- `programId` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **200**: KPI voucher\n\n**Errori documentati:** 400, 401, 403, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Vouchers.","parameters":[{"schema":{"type":"integer"},"in":"query","name":"programId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"KPI voucher","content":{"application/json":{"schema":{"description":"KPI voucher","type":"object","additionalProperties":true,"example":{"total":10,"active":3,"used":7}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/vouchers/sets":{"get":{"tags":["Vouchers"],"description":"Elenco lotti voucher (paginato). Per ogni lotto: issuedCount, usedCount, expiredCount.\n\n**Contesto:** I **Voucher/Coupon** sono codici sconto o benefit monouso. Si creano singolarmente, in lotti (set) o via promo; hanno validità, stato e tracciamento utilizzo.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Querystring:**\n- `programId` (integer) — opzionale\n- `setCode` (string) — opzionale\n- `generationMode` (string) — opzionale (valori: QUANTITY, AUDIENCE)\n- `page` (integer) — opzionale [default: 1]\n- `size` (integer) — opzionale [default: 20]\n\n**Risposte di successo:**\n- **200**: Lotti\n\n**Errori documentati:** 400, 401, 403, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Vouchers.","parameters":[{"schema":{"type":"integer"},"in":"query","name":"programId","required":false},{"schema":{"type":"string"},"in":"query","name":"setCode","required":false},{"schema":{"type":"string","enum":["QUANTITY","AUDIENCE"]},"in":"query","name":"generationMode","required":false},{"schema":{"type":"integer","default":1},"in":"query","name":"page","required":false},{"schema":{"type":"integer","default":20},"in":"query","name":"size","required":false}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Lotti","content":{"application/json":{"schema":{"description":"Lotti","type":"object","additionalProperties":true,"example":{"content":[{"id":1,"programId":1,"setCode":"LOTTO1","title":"Lotto","valueType":"FIXED","imageUrl":"https://cdn.example/voucher-lotto.png","createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}],"totalElements":1,"totalPages":1,"page":1,"size":20}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/vouchers/sets/{id}":{"get":{"tags":["Vouchers"],"description":"Dettaglio di sets per identificativo.\n\n**Contesto:** I **Voucher/Coupon** sono codici sconto o benefit monouso. Si creano singolarmente, in lotti (set) o via promo; hanno validità, stato e tracciamento utilizzo.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `id` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Lotto\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Vouchers.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Lotto","content":{"application/json":{"schema":{"description":"Lotto","type":"object","additionalProperties":true,"example":{"id":1,"programId":1,"setCode":"LOTTO1","title":"Lotto","valueType":"FIXED","imageUrl":"https://cdn.example/voucher-lotto.png","createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"patch":{"tags":["Vouchers"],"description":"Aggiorna parametri opzionali del lotto (extraData e/o imageUrl, max 2000 caratteri ciascuno).\n\n**Contesto:** I **Voucher/Coupon** sono codici sconto o benefit monouso. Si creano singolarmente, in lotti (set) o via promo; hanno validità, stato e tracciamento utilizzo.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `id` (integer) — obbligatorio\n\n**Body JSON:**\n- `extraData` (string) — opzionale: Testo opzionale (es. JSON), max 2000 caratteri\n- `imageUrl` (string) — opzionale: URL immagine lotto (es. S3), max 2000 caratteri\n\n**Risposte di successo:**\n- **200**: Lotto patch\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Vouchers.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"extraData":{"type":"string","description":"Testo opzionale (es. JSON), max 2000 caratteri","nullable":true},"imageUrl":{"type":"string","description":"URL immagine lotto (es. S3), max 2000 caratteri","nullable":true}}}}}},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Lotto patch","content":{"application/json":{"schema":{"description":"Lotto patch","type":"object","additionalProperties":true,"example":{"id":1,"programId":1,"setCode":"LOTTO1","title":"Lotto","valueType":"FIXED","imageUrl":"https://cdn.example/voucher-lotto.png","createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"put":{"tags":["Vouchers"],"description":"Aggiorna il lotto: merge dei campi inviati con i valori esistenti. Dopo `vouchersGenerated=true` non si possono modificare `generationMode`, `quantityPlanned`, `audienceId`. In modalità AUDIENCE (coupon non ancora emessi) `quantityPlanned` viene ricalcolato dal numero di clienti nell'audience.\n\n**Contesto:** I **Voucher/Coupon** sono codici sconto o benefit monouso. Si creano singolarmente, in lotti (set) o via promo; hanno validità, stato e tracciamento utilizzo.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `id` (integer) — obbligatorio\n\n**Body JSON:**\n- `title` (string) — opzionale\n- `description` (string) — opzionale\n- `valueType` (string) — opzionale (valori: FIXED, PERCENTAGE)\n- `discountValue` (number) — opzionale\n- `discountPercent` (number) — opzionale\n- `currencyId` (integer) — opzionale\n- `expiresAt` (string) — opzionale\n- `minimumAmount` (number) — opzionale\n- `allowedShopIds` (array) — opzionale\n- `allowedShopCategoryIds` (array) — opzionale\n- `promoExpiryDays` (integer) — opzionale\n- `extraData` (string) — opzionale\n- `imageUrl` (string) — opzionale: URL immagine lotto (es. S3), max 2000 caratteri\n- `generationMode` (string) — opzionale (valori: QUANTITY, AUDIENCE)\n- `quantityPlanned` (integer) — opzionale\n- `audienceId` (integer) — opzionale\n\n**Risposte di successo:**\n- **200**: Lotto aggiornato\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Vouchers.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"title":{"type":"string","nullable":true},"description":{"type":"string","nullable":true},"valueType":{"type":"string","enum":["FIXED","PERCENTAGE"]},"discountValue":{"type":"number"},"discountPercent":{"type":"number"},"currencyId":{"type":"integer","nullable":true},"expiresAt":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}]},"minimumAmount":{"type":"number","nullable":true},"allowedShopIds":{"type":"array","items":{"type":"integer"},"nullable":true},"allowedShopCategoryIds":{"type":"array","items":{"type":"integer"},"nullable":true},"productRestricted":{"type":"boolean"},"productIds":{"type":"array","items":{"type":"integer"},"nullable":true},"productCategoryIds":{"type":"array","items":{"type":"integer"},"nullable":true},"productApplyMode":{"type":"string","enum":["ONE_UNIT","ALL_UNITS","MAX_UNITS",null],"nullable":true},"productMaxUnits":{"type":"number","nullable":true},"promoExpiryDays":{"type":"integer","nullable":true},"promoExpiryMode":{"anyOf":[{"type":"string","enum":["DAYS","END_OF_MONTH"]},{"type":"null"}]},"extraData":{"type":"string","nullable":true},"imageUrl":{"type":"string","description":"URL immagine lotto (es. S3), max 2000 caratteri","nullable":true},"imageTemplate":{"description":"Template canvas JSON per generazione immagine voucher alla consegna","anyOf":[{"type":"object","additionalProperties":true},{"type":"null"}]},"walletBackgroundColor":{"type":"string","description":"Colore sfondo pass Phone Wallet (#RRGGBB o rgb())","nullable":true},"walletImageUrl":{"type":"string","description":"URL immagine strip/hero del pass Phone Wallet, max 2000 caratteri","nullable":true},"generationMode":{"type":"string","enum":["QUANTITY","AUDIENCE"]},"quantityPlanned":{"type":"integer","nullable":true},"audienceId":{"type":"integer","nullable":true}}}}}},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Lotto aggiornato","content":{"application/json":{"schema":{"description":"Lotto aggiornato","type":"object","additionalProperties":true,"example":{"id":1,"programId":1,"setCode":"LOTTO1","title":"Lotto","valueType":"FIXED","imageUrl":"https://cdn.example/voucher-lotto.png","createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/vouchers/sets/{id}/vouchersList":{"get":{"tags":["Vouchers"],"description":"Elenco paginato di vouchersList.\n\n**Contesto:** I **Voucher/Coupon** sono codici sconto o benefit monouso. Si creano singolarmente, in lotti (set) o via promo; hanno validità, stato e tracciamento utilizzo.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `id` (integer) — obbligatorio\n\n**Querystring:**\n- `page` (integer) — opzionale [default: 1]\n- `size` (integer) — opzionale [default: 20]\n- `status` (string) — opzionale\n\n**Risposte di successo:**\n- **200**: Voucher del lotto\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Vouchers.","parameters":[{"schema":{"type":"integer","default":1},"in":"query","name":"page","required":false},{"schema":{"type":"integer","default":20},"in":"query","name":"size","required":false},{"schema":{"type":"string"},"in":"query","name":"status","required":false},{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Voucher del lotto","content":{"application/json":{"schema":{"description":"Voucher del lotto","type":"object","additionalProperties":true,"example":{"content":[{"id":1,"code":"ABC123","programId":1,"status":"DELIVERED","title":"Voucher","valueType":"FIXED","discountValue":10,"createdAt":"2026-01-15T10:00:00.000Z"}],"totalElements":1,"totalPages":1,"page":1,"size":20}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/vouchers/by-id/{id}":{"get":{"tags":["Vouchers"],"description":"Dettaglio di by-id per identificativo.\n\n**Contesto:** I **Voucher/Coupon** sono codici sconto o benefit monouso. Si creano singolarmente, in lotti (set) o via promo; hanno validità, stato e tracciamento utilizzo.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `id` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Voucher per id\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Vouchers.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Voucher per id","content":{"application/json":{"schema":{"description":"Voucher per id","type":"object","additionalProperties":true,"example":{"id":1,"code":"ABC123","programId":1,"status":"DELIVERED","title":"Voucher","valueType":"FIXED","discountValue":10,"createdAt":"2026-01-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/vouchers/{code}":{"get":{"tags":["Vouchers"],"description":"Validate and get voucher details by code; optionally check totalMoney/shopId for validity.\n\n**Contesto:** I **Voucher/Coupon** sono codici sconto o benefit monouso. Si creano singolarmente, in lotti (set) o via promo; hanno validità, stato e tracciamento utilizzo.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `code` (string) — obbligatorio\n\n**Querystring:**\n- `totalMoney` (number) — opzionale\n- `shopId` (integer) — opzionale\n- `customerId` (integer) — opzionale\n\n**Risposte di successo:**\n- **200**: Voucher per codice\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Vouchers.","parameters":[{"schema":{"type":"number"},"in":"query","name":"totalMoney","required":false},{"schema":{"type":"integer"},"in":"query","name":"shopId","required":false},{"schema":{"type":"integer"},"in":"query","name":"customerId","required":false},{"schema":{"type":"string"},"in":"path","name":"code","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Voucher per codice","content":{"application/json":{"schema":{"description":"Voucher per codice","type":"object","additionalProperties":true,"example":{"id":1,"code":"ABC123","programId":1,"status":"DELIVERED","title":"Voucher","valueType":"FIXED","discountValue":10,"createdAt":"2026-01-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/vouchers/{code}/use":{"post":{"tags":["Vouchers"],"description":"Utilizza / riscatta un voucher o coupon per codice.\n\n**Contesto:** I **Voucher/Coupon** sono codici sconto o benefit monouso. Si creano singolarmente, in lotti (set) o via promo; hanno validità, stato e tracciamento utilizzo.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `code` (string) — obbligatorio\n\n**Body JSON:**\n- `movementId` (integer) — obbligatorio\n- `totalMoney` (number) — opzionale\n- `shopId` (integer) — opzionale\n- `customerId` (integer) — opzionale\n\n**Risposte di successo:**\n- **200**: Voucher utilizzato\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Vouchers.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["movementId"],"properties":{"movementId":{"type":"integer"},"totalMoney":{"type":"number"},"shopId":{"type":"integer"},"customerId":{"type":"integer"}}}}},"required":true},"parameters":[{"schema":{"type":"string"},"in":"path","name":"code","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Voucher utilizzato","content":{"application/json":{"schema":{"description":"Voucher utilizzato","type":"object","additionalProperties":true,"example":{"used":true,"movementId":1001}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/vouchers/customer/{customerId}":{"get":{"tags":["Vouchers"],"description":"List vouchers delivered to a customer. Token CUSTOMER: solo se :customerId coincide con il sub del JWT. Token POS: qualsiasi cliente del programma; solo voucher utilizzabili nel negozio del token (allowed_shop_ids e/o allowed_shop_category_ids con match gerarchico categoria/sottocategoria).\n\n**Contesto:** I **Voucher/Coupon** sono codici sconto o benefit monouso. Si creano singolarmente, in lotti (set) o via promo; hanno validità, stato e tracciamento utilizzo.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `customerId` (integer) — obbligatorio\n\n**Querystring:**\n- `status` (string) — opzionale (valori: PENDING, DELIVERED, USED, EXPIRED, CANCELLED)\n- `page` (integer) — opzionale [default: 1]\n- `size` (integer) — opzionale [default: 20]\n\n**Risposte di successo:**\n- **200**: Voucher cliente\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Vouchers.","parameters":[{"schema":{"type":"string","enum":["PENDING","DELIVERED","USED","EXPIRED","CANCELLED"]},"in":"query","name":"status","required":false},{"schema":{"type":"integer","default":1},"in":"query","name":"page","required":false},{"schema":{"type":"integer","default":20},"in":"query","name":"size","required":false},{"schema":{"type":"integer"},"in":"path","name":"customerId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Voucher cliente","content":{"application/json":{"schema":{"description":"Voucher cliente","type":"object","additionalProperties":true,"example":{"content":[{"id":1,"code":"ABC123","programId":1,"status":"DELIVERED","title":"Voucher","valueType":"FIXED","discountValue":10,"createdAt":"2026-01-15T10:00:00.000Z"}],"totalElements":1,"totalPages":1,"page":1,"size":20}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/news-categories":{"get":{"tags":["News"],"description":"Elenco paginato di news-categories del programma indicato in `programId`.\n\n**Contesto:** Le **News** sono comunicazioni editoriali del programma (promo, eventi, avvisi). Possono essere filtrate per audience, negozi e periodo di validità.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n\n**Querystring:**\n- `page` (integer) — opzionale [default: 1]\n- `size` (integer) — opzionale [default: 50]\n- `search` (string) — opzionale\n\n**Risposte di successo:**\n- **200**: Lista news-categories\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** News.","parameters":[{"schema":{"type":"integer","default":1},"in":"query","name":"page","required":false},{"schema":{"type":"integer","default":50},"in":"query","name":"size","required":false},{"schema":{"type":"string"},"in":"query","name":"search","required":false},{"schema":{"type":"integer"},"in":"path","name":"programId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Lista news-categories","content":{"application/json":{"schema":{"description":"Lista news-categories","type":"object","additionalProperties":true,"example":{"content":[{"id":1,"name":"Esempio","programId":1,"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}],"totalElements":1,"totalPages":1,"page":1,"size":20}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"post":{"tags":["News"],"description":"Crea news-categories del programma indicato in `programId`.\n\n**Contesto:** Le **News** sono comunicazioni editoriali del programma (promo, eventi, avvisi). Possono essere filtrate per audience, negozi e periodo di validità.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n\n**Body JSON:**\n- `name` (string) — obbligatorio\n\n**Risposte di successo:**\n- **201**: Risorsa creata (news-categories)\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** News.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["name"],"properties":{"name":{"type":"string"}}}}},"required":true},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"201":{"description":"Risorsa creata (news-categories)","content":{"application/json":{"schema":{"description":"Risorsa creata (news-categories)","type":"object","additionalProperties":true,"example":{"id":1,"name":"Esempio","programId":1,"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/news-categories/{id}":{"put":{"tags":["News"],"description":"Aggiorna una programma fedeltà (campi consentiti) del programma indicato in `programId`.\n\n**Contesto:** Le **News** sono comunicazioni editoriali del programma (promo, eventi, avvisi). Possono essere filtrate per audience, negozi e periodo di validità.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n- `id` (integer) — obbligatorio\n\n**Body JSON:**\n- `name` (string) — opzionale\n\n**Risposte di successo:**\n- **200**: Aggiornamento news-categories\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** News.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"}}}}}},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Aggiornamento news-categories","content":{"application/json":{"schema":{"description":"Aggiornamento news-categories","type":"object","additionalProperties":true,"example":{"id":1,"name":"Esempio","programId":1,"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"delete":{"tags":["News"],"description":"Elimina una programma fedeltà del programma indicato in `programId` (soft delete o `TERMINATED` se vincoli FK).\n\n**Contesto:** Le **News** sono comunicazioni editoriali del programma (promo, eventi, avvisi). Possono essere filtrate per audience, negozi e periodo di validità.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n- `id` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **204**: Operazione riuscita senza corpo di risposta.\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** News.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"204":{"description":"Operazione riuscita senza corpo di risposta."},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/news":{"get":{"tags":["News"],"description":"Elenco paginato di news del programma indicato in `programId`.\n\n**Contesto:** Le **News** sono comunicazioni editoriali del programma (promo, eventi, avvisi). Possono essere filtrate per audience, negozi e periodo di validità.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n\n**Querystring:**\n- `netId` (integer) — opzionale\n- `shopId` (integer) — opzionale\n- `categoryId` (integer) — opzionale\n- `page` (integer) — opzionale [default: 1]\n- `size` (integer) — opzionale [default: 20]\n- `search` (string) — opzionale\n- `enabledOnly` (boolean) — opzionale\n- `eventsOnly` (boolean) — opzionale\n\n**Risposte di successo:**\n- **200**: Lista news\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** News.","parameters":[{"schema":{"type":"integer"},"in":"query","name":"netId","required":false},{"schema":{"type":"integer"},"in":"query","name":"shopId","required":false},{"schema":{"type":"integer"},"in":"query","name":"categoryId","required":false},{"schema":{"type":"integer","default":1},"in":"query","name":"page","required":false},{"schema":{"type":"integer","default":20},"in":"query","name":"size","required":false},{"schema":{"type":"string"},"in":"query","name":"search","required":false},{"schema":{"type":"boolean"},"in":"query","name":"enabledOnly","required":false},{"schema":{"type":"boolean"},"in":"query","name":"eventsOnly","required":false},{"schema":{"type":"integer"},"in":"path","name":"programId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Lista news","content":{"application/json":{"schema":{"description":"Lista news","type":"object","additionalProperties":true,"example":{"content":[{"id":1,"programId":1,"title":"Promozione estate","shortText":"Scopri le novità","bodyHtml":"<p>Testo dettagliato</p>","categoryId":1,"validFrom":"2026-01-15T10:00:00.000Z","validTo":"2026-01-15T10:00:00.000Z","featured":false,"displayOrder":0,"isEvent":false,"enabled":true,"shopIds":[],"audienceIds":[],"imageUrls":[],"readByCustomer":false,"readAt":null,"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}],"totalElements":1,"totalPages":1,"page":1,"size":20}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"post":{"tags":["News"],"description":"Crea news del programma indicato in `programId`.\n\n**Contesto:** Le **News** sono comunicazioni editoriali del programma (promo, eventi, avvisi). Possono essere filtrate per audience, negozi e periodo di validità.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n\n**Body JSON:**\n- `title` (string) — obbligatorio\n- `shortText` (string) — opzionale\n- `bodyHtml` (string) — opzionale\n- `categoryId` (integer) — opzionale\n- `validFrom` (string) — opzionale\n- `validTo` (string) — opzionale\n- `featured` (boolean) — opzionale\n- `displayOrder` (integer) — opzionale\n- `isEvent` (boolean) — opzionale\n- `eventStartAt` (string) — opzionale\n- `eventEndAt` (string) — opzionale\n- `eventAddress` (string) — opzionale\n- `eventLatitude` (number) — opzionale\n- `eventLongitude` (number) — opzionale\n- `shopIds` (array) — opzionale\n- `audienceIds` (array) — opzionale\n- `imageUrls` (array) — opzionale\n- `linkUrl` (string) — opzionale\n- `videoUrl` (string) — opzionale\n- `enabled` (boolean) — opzionale\n- `netId` (integer) — opzionale\n- `shopId` (integer) — opzionale\n\n**Risposte di successo:**\n- **201**: Risorsa creata (news)\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** News.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["title"],"properties":{"title":{"type":"string"},"shortText":{"type":"string"},"bodyHtml":{"type":"string"},"categoryId":{"type":"integer","nullable":true},"validFrom":{"type":"string","format":"date-time","nullable":true},"validTo":{"type":"string","format":"date-time","nullable":true},"featured":{"type":"boolean"},"displayOrder":{"type":"integer"},"isEvent":{"type":"boolean"},"eventStartAt":{"type":"string","format":"date-time","nullable":true},"eventEndAt":{"type":"string","format":"date-time","nullable":true},"eventAddress":{"type":"string","nullable":true},"eventLatitude":{"type":"number","nullable":true},"eventLongitude":{"type":"number","nullable":true},"shopIds":{"type":"array","items":{"type":"integer"}},"audienceIds":{"type":"array","items":{"type":"integer"}},"imageUrls":{"type":"array","items":{"type":"string"}},"linkUrl":{"type":"string","nullable":true},"videoUrl":{"type":"string","nullable":true},"enabled":{"type":"boolean"},"netId":{"type":"integer","nullable":true},"shopId":{"type":"integer","nullable":true}}}}},"required":true},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"201":{"description":"Risorsa creata (news)","content":{"application/json":{"schema":{"description":"Risorsa creata (news)","type":"object","additionalProperties":true,"example":{"id":1,"programId":1,"title":"Promozione estate","shortText":"Scopri le novità","bodyHtml":"<p>Testo dettagliato</p>","categoryId":1,"validFrom":"2026-01-15T10:00:00.000Z","validTo":"2026-01-15T10:00:00.000Z","featured":false,"displayOrder":0,"isEvent":false,"enabled":true,"shopIds":[],"audienceIds":[],"imageUrls":[],"readByCustomer":false,"readAt":null,"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/news/feed":{"get":{"tags":["News"],"description":"Elenco paginato di feed del programma indicato in `programId`.\n\n**Contesto:** Le **News** sono comunicazioni editoriali del programma (promo, eventi, avvisi). Possono essere filtrate per audience, negozi e periodo di validità.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n\n**Querystring:**\n- `customerId` (integer) — opzionale\n- `page` (integer) — opzionale [default: 1]\n- `size` (integer) — opzionale [default: 20]\n- `categoryId` (integer) — opzionale\n- `eventsOnly` (boolean) — opzionale\n\n**Risposte di successo:**\n- **200**: Feed news per clienti (visibilità e targeting).\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** News.","parameters":[{"schema":{"type":"integer"},"in":"query","name":"customerId","required":false},{"schema":{"type":"integer","default":1},"in":"query","name":"page","required":false},{"schema":{"type":"integer","default":20},"in":"query","name":"size","required":false},{"schema":{"type":"integer"},"in":"query","name":"categoryId","required":false},{"schema":{"type":"boolean"},"in":"query","name":"eventsOnly","required":false},{"schema":{"type":"integer"},"in":"path","name":"programId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Feed news per clienti (visibilità e targeting).","content":{"application/json":{"schema":{"description":"Feed news per clienti (visibilità e targeting).","type":"object","additionalProperties":true,"example":{"content":[{"id":1,"programId":1,"title":"Promozione estate","shortText":"Scopri le novità","bodyHtml":"<p>Testo</p>","isEvent":false,"enabled":true,"likesCount":12,"likedByCustomer":false,"rsvpStatus":null,"readByCustomer":false,"readAt":null,"imageUrls":[],"images":[{"id":1,"url":"https://cdn.example/news.jpg","sortOrder":0}],"shopIds":[],"audienceIds":[],"createdAt":"2026-05-15T10:00:00.000Z","updatedAt":"2026-05-15T10:00:00.000Z"}],"totalElements":1,"totalPages":1,"page":1,"size":20}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/news/events":{"get":{"tags":["News"],"description":"Elenco paginato di events del programma indicato in `programId`.\n\n**Contesto:** Le **News** sono comunicazioni editoriali del programma (promo, eventi, avvisi). Possono essere filtrate per audience, negozi e periodo di validità.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n\n**Querystring:**\n- `from` (string) — opzionale\n- `to` (string) — opzionale\n- `customerId` (integer) — opzionale\n\n**Risposte di successo:**\n- **200**: Calendario eventi (is_event = true).\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** News.","parameters":[{"schema":{"type":"string","format":"date-time"},"in":"query","name":"from","required":false},{"schema":{"type":"string","format":"date-time"},"in":"query","name":"to","required":false},{"schema":{"type":"integer"},"in":"query","name":"customerId","required":false},{"schema":{"type":"integer"},"in":"path","name":"programId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Calendario eventi (is_event = true).","content":{"application/json":{"schema":{"description":"Calendario eventi (is_event = true).","type":"object","additionalProperties":true,"example":{"items":[{"id":2,"programId":1,"title":"Evento inaugurazione","isEvent":true,"eventStartAt":"2026-05-15T10:00:00.000Z","eventEndAt":"2026-05-15T10:00:00.000Z","eventAddress":"Via Roma 1, Firenze","eventLatitude":43.77,"eventLongitude":11.25,"likesCount":5,"likedByCustomer":true,"rsvpStatus":"YES","readByCustomer":true,"readAt":"2026-05-15T10:00:00.000Z","createdAt":"2026-05-15T10:00:00.000Z","updatedAt":"2026-05-15T10:00:00.000Z"}]}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/news/{id}":{"get":{"tags":["News"],"description":"Dettaglio di una programma fedeltà per identificativo del programma indicato in `programId`.\n\n**Contesto:** Le **News** sono comunicazioni editoriali del programma (promo, eventi, avvisi). Possono essere filtrate per audience, negozi e periodo di validità.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n- `id` (integer) — obbligatorio\n\n**Querystring:**\n- `customerId` (integer) — opzionale\n\n**Risposte di successo:**\n- **200**: Dettaglio news\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** News.","parameters":[{"schema":{"type":"integer"},"in":"query","name":"customerId","required":false},{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Dettaglio news","content":{"application/json":{"schema":{"description":"Dettaglio news","type":"object","additionalProperties":true,"example":{"id":1,"programId":1,"title":"Promozione estate","shortText":"Scopri le novità","bodyHtml":"<p>Testo dettagliato</p>","categoryId":1,"validFrom":"2026-01-15T10:00:00.000Z","validTo":"2026-01-15T10:00:00.000Z","featured":false,"displayOrder":0,"isEvent":false,"enabled":true,"shopIds":[],"audienceIds":[],"imageUrls":[],"readByCustomer":false,"readAt":null,"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"put":{"tags":["News"],"description":"Aggiorna una programma fedeltà (campi consentiti) del programma indicato in `programId`.\n\n**Contesto:** Le **News** sono comunicazioni editoriali del programma (promo, eventi, avvisi). Possono essere filtrate per audience, negozi e periodo di validità.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n- `id` (integer) — obbligatorio\n\n**Body JSON:**\n- `title` (string) — opzionale\n- `shortText` (string) — opzionale\n- `bodyHtml` (string) — opzionale\n- `categoryId` (integer) — opzionale\n- `validFrom` (string) — opzionale\n- `validTo` (string) — opzionale\n- `featured` (boolean) — opzionale\n- `displayOrder` (integer) — opzionale\n- `isEvent` (boolean) — opzionale\n- `eventStartAt` (string) — opzionale\n- `eventEndAt` (string) — opzionale\n- `eventAddress` (string) — opzionale\n- `eventLatitude` (number) — opzionale\n- `eventLongitude` (number) — opzionale\n- `shopIds` (array) — opzionale\n- `audienceIds` (array) — opzionale\n- `imageUrls` (array) — opzionale\n- `linkUrl` (string) — opzionale\n- `videoUrl` (string) — opzionale\n- `enabled` (boolean) — opzionale\n- `netId` (integer) — opzionale\n- `shopId` (integer) — opzionale\n\n**Risposte di successo:**\n- **200**: Aggiornamento news\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** News.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"title":{"type":"string"},"shortText":{"type":"string"},"bodyHtml":{"type":"string"},"categoryId":{"type":"integer","nullable":true},"validFrom":{"type":"string","format":"date-time","nullable":true},"validTo":{"type":"string","format":"date-time","nullable":true},"featured":{"type":"boolean"},"displayOrder":{"type":"integer"},"isEvent":{"type":"boolean"},"eventStartAt":{"type":"string","format":"date-time","nullable":true},"eventEndAt":{"type":"string","format":"date-time","nullable":true},"eventAddress":{"type":"string","nullable":true},"eventLatitude":{"type":"number","nullable":true},"eventLongitude":{"type":"number","nullable":true},"shopIds":{"type":"array","items":{"type":"integer"}},"audienceIds":{"type":"array","items":{"type":"integer"}},"imageUrls":{"type":"array","items":{"type":"string"}},"linkUrl":{"type":"string","nullable":true},"videoUrl":{"type":"string","nullable":true},"enabled":{"type":"boolean"},"netId":{"type":"integer","nullable":true},"shopId":{"type":"integer","nullable":true}}}}}},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Aggiornamento news","content":{"application/json":{"schema":{"description":"Aggiornamento news","type":"object","additionalProperties":true,"example":{"id":1,"programId":1,"title":"Promozione estate","shortText":"Scopri le novità","bodyHtml":"<p>Testo dettagliato</p>","categoryId":1,"validFrom":"2026-01-15T10:00:00.000Z","validTo":"2026-01-15T10:00:00.000Z","featured":false,"displayOrder":0,"isEvent":false,"enabled":true,"shopIds":[],"audienceIds":[],"imageUrls":[],"readByCustomer":false,"readAt":null,"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"delete":{"tags":["News"],"description":"Elimina una programma fedeltà del programma indicato in `programId` (soft delete o `TERMINATED` se vincoli FK).\n\n**Contesto:** Le **News** sono comunicazioni editoriali del programma (promo, eventi, avvisi). Possono essere filtrate per audience, negozi e periodo di validità.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n- `id` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **204**: Operazione riuscita senza corpo di risposta.\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** News.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"204":{"description":"Operazione riuscita senza corpo di risposta."},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/cms":{"get":{"tags":["Cms"],"description":"Catalogo dei tipi CMS nuovi (schema). Non interroga Strapi.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Lista cms","content":{"application/json":{"schema":{"description":"Lista cms","type":"object","additionalProperties":true,"example":{"content":[{"id":1,"name":"Esempio","programId":1,"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}],"totalElements":1,"totalPages":1,"page":1,"size":20}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/cms/media":{"post":{"tags":["Cms"],"description":"Carica un file sul media library di Strapi (immagini o PDF). Non modifica contenuti esistenti.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/cms/{type}":{"get":{"tags":["Cms"],"description":"Elenco contenuti del programma. Filtra sempre per programId. status=draft|published (default draft).","parameters":[{"schema":{"type":"integer","default":1},"in":"query","name":"page","required":false},{"schema":{"type":"integer","default":25},"in":"query","name":"size","required":false},{"schema":{"type":"string","enum":["draft","published"]},"in":"query","name":"status","required":false},{"schema":{"type":"string"},"in":"query","name":"locale","required":false},{"schema":{"type":"string","enum":["app","web"]},"in":"query","name":"channel","required":false},{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"string","enum":["app-release","banners","advertising","legal-documents","status-tiers"]},"in":"path","name":"type","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Dettaglio cms","content":{"application/json":{"schema":{"description":"Dettaglio cms","type":"object","additionalProperties":true,"example":{"id":1,"name":"Esempio","programId":1,"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"post":{"tags":["Cms"],"description":"Crea un contenuto in bozza. programId è quello del path. publish=true pubblica subito.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"string","enum":["app-release","banners","advertising","legal-documents","status-tiers"]},"in":"path","name":"type","required":true}],"security":[{"bearerAuth":[]}],"responses":{"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/cms/{type}/{documentId}":{"get":{"tags":["Cms"],"description":"Dettaglio di un contenuto del programma.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"string","enum":["app-release","banners","advertising","legal-documents","status-tiers"]},"in":"path","name":"type","required":true},{"schema":{"type":"string"},"in":"path","name":"documentId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"put":{"tags":["Cms"],"description":"Aggiorna la bozza. Rifiuta il documento se non appartiene al programma.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"string","enum":["app-release","banners","advertising","legal-documents","status-tiers"]},"in":"path","name":"type","required":true},{"schema":{"type":"string"},"in":"path","name":"documentId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"delete":{"tags":["Cms"],"description":"Elimina un contenuto del programma.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"string","enum":["app-release","banners","advertising","legal-documents","status-tiers"]},"in":"path","name":"type","required":true},{"schema":{"type":"string"},"in":"path","name":"documentId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"204":{"description":"Operazione riuscita senza corpo di risposta."},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/cms/{type}/{documentId}/publish":{"post":{"tags":["Cms"],"description":"Pubblica la bozza. Sui banner si può indicare locale.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"string","enum":["app-release","banners","advertising","legal-documents","status-tiers"]},"in":"path","name":"type","required":true},{"schema":{"type":"string"},"in":"path","name":"documentId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/cms/{type}/{documentId}/unpublish":{"post":{"tags":["Cms"],"description":"Toglie la pubblicazione. La bozza resta.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"string","enum":["app-release","banners","advertising","legal-documents","status-tiers"]},"in":"path","name":"type","required":true},{"schema":{"type":"string"},"in":"path","name":"documentId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/customers/{customerId}/news/{newsId}/like":{"post":{"summary":"Toggle like on news","tags":["News"],"description":"Crea like.\n\n**Contesto:** Le **News** sono comunicazioni editoriali del programma (promo, eventi, avvisi). Possono essere filtrate per audience, negozi e periodo di validità.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `customerId` (integer) — obbligatorio\n- `newsId` (integer) — obbligatorio\n\n**Body JSON:**\n- `programId` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Toggle like su una news.\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** News.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["programId"],"properties":{"programId":{"type":"integer"}}}}},"required":true},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"customerId","required":true},{"schema":{"type":"integer"},"in":"path","name":"newsId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Toggle like su una news.","content":{"application/json":{"schema":{"description":"Toggle like su una news.","type":"object","additionalProperties":true,"example":{"newsId":1,"customerId":42,"liked":true,"likesCount":13}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/customers/{customerId}/news/{newsId}/read":{"post":{"summary":"Mark news as read by customer","tags":["News"],"description":"Segna la news come letta. Idempotente: le chiamate successive conservano il primo readAt.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["programId"],"properties":{"programId":{"type":"integer"}}}}},"required":true},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"customerId","required":true},{"schema":{"type":"integer"},"in":"path","name":"newsId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"News segnata come letta. Idempotente: readAt resta quello della prima lettura.","content":{"application/json":{"schema":{"description":"News segnata come letta. Idempotente: readAt resta quello della prima lettura.","type":"object","additionalProperties":true,"example":{"newsId":1,"customerId":42,"read":true,"readAt":"2026-05-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/customers/{customerId}/news/unread-count":{"get":{"summary":"Unread news count for customer feed","tags":["News"],"description":"Conta le news visibili nel feed del customer che non hanno una riga in news_reads.","parameters":[{"schema":{"type":"integer"},"in":"query","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"customerId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"News visibili nel feed e non ancora lette dal customer.","content":{"application/json":{"schema":{"description":"News visibili nel feed e non ancora lette dal customer.","type":"object","additionalProperties":true,"example":{"customerId":42,"programId":1,"unreadCount":3}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/customers/{customerId}/news/{newsId}/rsvp":{"put":{"summary":"Set event RSVP (YES, NO, MAYBE)","tags":["News"],"description":"Aggiorna rsvp (campi consentiti).\n\n**Contesto:** Le **News** sono comunicazioni editoriali del programma (promo, eventi, avvisi). Possono essere filtrate per audience, negozi e periodo di validità.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `customerId` (integer) — obbligatorio\n- `newsId` (integer) — obbligatorio\n\n**Body JSON:**\n- `programId` (integer) — obbligatorio\n- `status` (string) — obbligatorio (valori: YES, NO, MAYBE)\n\n**Risposte di successo:**\n- **200**: Imposta RSVP evento (YES, NO, MAYBE).\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** News.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["programId","status"],"properties":{"programId":{"type":"integer"},"status":{"type":"string","enum":["YES","NO","MAYBE"]}}}}},"required":true},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"customerId","required":true},{"schema":{"type":"integer"},"in":"path","name":"newsId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Imposta RSVP evento (YES, NO, MAYBE).","content":{"application/json":{"schema":{"description":"Imposta RSVP evento (YES, NO, MAYBE).","type":"object","additionalProperties":true,"example":{"newsId":2,"customerId":42,"status":"YES"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/customers/{customerId}/news/{newsId}/interactions":{"post":{"summary":"Track interaction (link click, video, add to calendar)","tags":["News"],"description":"Crea interactions.\n\n**Contesto:** Le **News** sono comunicazioni editoriali del programma (promo, eventi, avvisi). Possono essere filtrate per audience, negozi e periodo di validità.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `customerId` (integer) — obbligatorio\n- `newsId` (integer) — obbligatorio\n\n**Body JSON:**\n- `programId` (integer) — obbligatorio\n- `interactionType` (string) — obbligatorio (valori: LINK_CLICK, VIDEO_PLAY, VIDEO_PROGRESS, ADD_TO_CALENDAR)\n- `metadata` (object) — opzionale\n\n**Risposte di successo:**\n- **201**: Traccia interazione (click link, video, calendario).\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** News.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["programId","interactionType"],"properties":{"programId":{"type":"integer"},"interactionType":{"type":"string","enum":["LINK_CLICK","VIDEO_PLAY","VIDEO_PROGRESS","ADD_TO_CALENDAR"]},"metadata":{"type":"object"}}}}},"required":true},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"customerId","required":true},{"schema":{"type":"integer"},"in":"path","name":"newsId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"201":{"description":"Traccia interazione (click link, video, calendario).","content":{"application/json":{"schema":{"description":"Traccia interazione (click link, video, calendario).","type":"object","additionalProperties":true,"example":{"id":1,"newsId":1,"customerId":42,"interactionType":"LINK_CLICK","metadata":{"url":"https://example.com"},"createdAt":"2026-05-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/coupon-offers":{"get":{"tags":["CouponOffers"],"description":"Elenco paginato di offerta coupon del programma indicato in `programId`.\n\n**Contesto:** Le **Offerte coupon** sono deal vendibili (sconto cena, servizio) con varianti, slot di validità, pagamento wallet/denaro e integrazione Stripe.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (string) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Lista coupon-offers\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** CouponOffers.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"in":"path","name":"programId","required":true}],"responses":{"200":{"description":"Lista coupon-offers","content":{"application/json":{"schema":{"description":"Lista coupon-offers","type":"object","additionalProperties":true,"example":{"content":[{"id":1,"name":"Esempio","programId":1,"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}],"totalElements":1,"totalPages":1,"page":1,"size":20}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"post":{"tags":["CouponOffers"],"description":"Crea offerta coupon del programma indicato in `programId`.\n\n**Contesto:** Le **Offerte coupon** sono deal vendibili (sconto cena, servizio) con varianti, slot di validità, pagamento wallet/denaro e integrazione Stripe.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (string) — obbligatorio\n\n**Risposte di successo:**\n- **201**: Crea una nuova offerta coupon.\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** CouponOffers.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"in":"path","name":"programId","required":true}],"responses":{"201":{"description":"Crea una nuova offerta coupon.","content":{"application/json":{"schema":{"description":"Crea una nuova offerta coupon.","type":"object","additionalProperties":true,"example":{"id":10,"programId":1,"shopId":3,"shopName":"Ristorante Demo","shopProvince":"FI","title":"Sconto 20% cena","descriptionHtml":"<p>Descrizione offerta</p>","conditionsHtml":"<p>Condizioni</p>","mainImageUrl":"https://cdn.example/offer.jpg","publishFrom":"2026-05-15T10:00:00.000Z","publishTo":null,"validFrom":"2026-05-15T10:00:00.000Z","validTo":"2026-12-31T23:59:59.000Z","excludeExistingCustomers":false,"adminStatus":"ACTIVE","lifecycleStatus":"ACTIVE","couponsSold":5,"couponsUsed":2,"galleryImages":[{"url":"https://cdn.example/g1.jpg","sortOrder":0}],"mediaLinks":[{"url":"https://example.com/menu","label":"Menu","sortOrder":0}],"validitySlots":[{"id":1,"weekdays":[5,6],"timeFrom":"18:00","timeTo":"23:00","sortOrder":0}],"variants":[{"id":20,"offerId":10,"description":"Cena per 2","couponQuantity":100,"soldCount":5,"remaining":95,"offerPrice":25,"listPrice":50,"discountPercent":50,"imageUrl":null,"sortOrder":0,"paymentOption":{"id":30,"variantId":20,"name":null,"sortOrder":0,"walletId":1,"walletUnits":250,"moneyAmount":null,"currencyId":null}}],"createdAt":"2026-05-15T10:00:00.000Z","updatedAt":"2026-05-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/coupon-offers/{id}":{"get":{"tags":["CouponOffers"],"description":"Dettaglio di una programma fedeltà per identificativo del programma indicato in `programId`.\n\n**Contesto:** Le **Offerte coupon** sono deal vendibili (sconto cena, servizio) con varianti, slot di validità, pagamento wallet/denaro e integrazione Stripe.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (string) — obbligatorio\n- `id` (string) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Dettaglio offerta coupon con varianti, galleria e slot di validità.\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** CouponOffers.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"in":"path","name":"programId","required":true},{"schema":{"type":"string"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Dettaglio offerta coupon con varianti, galleria e slot di validità.","content":{"application/json":{"schema":{"description":"Dettaglio offerta coupon con varianti, galleria e slot di validità.","type":"object","additionalProperties":true,"example":{"id":10,"programId":1,"shopId":3,"shopName":"Ristorante Demo","shopProvince":"FI","title":"Sconto 20% cena","descriptionHtml":"<p>Descrizione offerta</p>","conditionsHtml":"<p>Condizioni</p>","mainImageUrl":"https://cdn.example/offer.jpg","publishFrom":"2026-05-15T10:00:00.000Z","publishTo":null,"validFrom":"2026-05-15T10:00:00.000Z","validTo":"2026-12-31T23:59:59.000Z","excludeExistingCustomers":false,"adminStatus":"ACTIVE","lifecycleStatus":"ACTIVE","couponsSold":5,"couponsUsed":2,"galleryImages":[{"url":"https://cdn.example/g1.jpg","sortOrder":0}],"mediaLinks":[{"url":"https://example.com/menu","label":"Menu","sortOrder":0}],"validitySlots":[{"id":1,"weekdays":[5,6],"timeFrom":"18:00","timeTo":"23:00","sortOrder":0}],"variants":[{"id":20,"offerId":10,"description":"Cena per 2","couponQuantity":100,"soldCount":5,"remaining":95,"offerPrice":25,"listPrice":50,"discountPercent":50,"imageUrl":null,"sortOrder":0,"paymentOption":{"id":30,"variantId":20,"name":null,"sortOrder":0,"walletId":1,"walletUnits":250,"moneyAmount":null,"currencyId":null}}],"createdAt":"2026-05-15T10:00:00.000Z","updatedAt":"2026-05-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"put":{"tags":["CouponOffers"],"description":"Aggiorna una programma fedeltà (campi consentiti) del programma indicato in `programId`.\n\n**Contesto:** Le **Offerte coupon** sono deal vendibili (sconto cena, servizio) con varianti, slot di validità, pagamento wallet/denaro e integrazione Stripe.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (string) — obbligatorio\n- `id` (string) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Aggiorna un'offerta coupon esistente.\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** CouponOffers.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"in":"path","name":"programId","required":true},{"schema":{"type":"string"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Aggiorna un'offerta coupon esistente.","content":{"application/json":{"schema":{"description":"Aggiorna un'offerta coupon esistente.","type":"object","additionalProperties":true,"example":{"id":10,"programId":1,"shopId":3,"shopName":"Ristorante Demo","shopProvince":"FI","title":"Sconto 20% cena","descriptionHtml":"<p>Descrizione offerta</p>","conditionsHtml":"<p>Condizioni</p>","mainImageUrl":"https://cdn.example/offer.jpg","publishFrom":"2026-05-15T10:00:00.000Z","publishTo":null,"validFrom":"2026-05-15T10:00:00.000Z","validTo":"2026-12-31T23:59:59.000Z","excludeExistingCustomers":false,"adminStatus":"ACTIVE","lifecycleStatus":"ACTIVE","couponsSold":5,"couponsUsed":2,"galleryImages":[{"url":"https://cdn.example/g1.jpg","sortOrder":0}],"mediaLinks":[{"url":"https://example.com/menu","label":"Menu","sortOrder":0}],"validitySlots":[{"id":1,"weekdays":[5,6],"timeFrom":"18:00","timeTo":"23:00","sortOrder":0}],"variants":[{"id":20,"offerId":10,"description":"Cena per 2","couponQuantity":100,"soldCount":5,"remaining":95,"offerPrice":25,"listPrice":50,"discountPercent":50,"imageUrl":null,"sortOrder":0,"paymentOption":{"id":30,"variantId":20,"name":null,"sortOrder":0,"walletId":1,"walletUnits":250,"moneyAmount":null,"currencyId":null}}],"createdAt":"2026-05-15T10:00:00.000Z","updatedAt":"2026-05-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"delete":{"tags":["CouponOffers"],"description":"Elimina una programma fedeltà del programma indicato in `programId` (soft delete o `TERMINATED` se vincoli FK).\n\n**Contesto:** Le **Offerte coupon** sono deal vendibili (sconto cena, servizio) con varianti, slot di validità, pagamento wallet/denaro e integrazione Stripe.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (string) — obbligatorio\n- `id` (string) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Elimina logicamente un'offerta coupon.\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** CouponOffers.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"in":"path","name":"programId","required":true},{"schema":{"type":"string"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Elimina logicamente un'offerta coupon.","content":{"application/json":{"schema":{"description":"Elimina logicamente un'offerta coupon.","type":"object","additionalProperties":true,"example":{"id":10,"deleted":true}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/coupon-offers/{offerId}/purchase":{"post":{"summary":"Purchase a coupon offer variant (wallet and/or Stripe)","tags":["CouponOffers"],"description":"Acquista una variante (gift card, coupon, ecc.) con wallet o Stripe.\n\n**Contesto:** Le **Offerte coupon** sono deal vendibili (sconto cena, servizio) con varianti, slot di validità, pagamento wallet/denaro e integrazione Stripe.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (string) — obbligatorio\n- `offerId` (string) — obbligatorio\n\n**Body JSON:**\n- `variantId` (integer) — obbligatorio: ID variante coupon da acquistare\n- `customerId` (integer) — opzionale: Obbligatorio per operatori; ignorato se token CUSTOMER\n- `paymentOptionId` (integer) — opzionale: ID opzione di pagamento (`coupon_offer_payment_options.id`) configurata sulla variante in admin. Definisce wallet, valuta e ripartizione predefinita (walletUnits + stripeAmount). Opzionale: se omesso si usa l'unica opzione della variante. Se inviato, deve coincidere con quella configurata.\n- `walletUnits` (number) — opzionale: Unità wallet da addebitare sul cliente\n- `stripeAmount` (number) — opzionale: Importo in valuta da pagare con Stripe. Se > 0 viene aperta una Checkout Session. walletUnits + stripeAmount deve essere uguale al prezzo della variante (`offerPrice`).\n- `moneyAmount` (number) — opzionale: Alias legacy di `stripeAmount` (stesso significato).\n- `successUrl` (string) — opzionale: URL redirect dopo pagamento Stripe\n- `cancelUrl` (string) — opzionale: URL redirect se pagamento Stripe annullato\n- `captureMode` (string) — opzionale: automatic = addebito immediato su Stripe; manual = autorizzazione/blocco fondi, capture al webhook dopo scalata wallet (valori: automatic, manual) [default: \"automatic\"]\n- `present` (object) — opzionale: Regalo opzionale per un terzo. Se `destEmailTo` o `destPhoneTo` sono valorizzati, l'acquisto è trattato come regalo: il destinatario riceve notifiche e il coupon/card.\n\n**Risposte di successo:**\n- **200**: Acquisto coupon con moneyAmount > 0: sessione Stripe Checkout.\n- **201**: Acquisto coupon completato con wallet (status ISSUED). Supporta regalo opzionale.\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** CouponOffers.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["variantId"],"properties":{"variantId":{"type":"integer","description":"ID variante coupon da acquistare"},"customerId":{"type":"integer","description":"Obbligatorio per operatori; ignorato se token CUSTOMER"},"paymentOptionId":{"type":"integer","description":"ID opzione di pagamento (`coupon_offer_payment_options.id`) configurata sulla variante in admin. Definisce wallet, valuta e ripartizione predefinita (walletUnits + stripeAmount). Opzionale: se omesso si usa l'unica opzione della variante. Se inviato, deve coincidere con quella configurata."},"walletUnits":{"type":"number","description":"Unità wallet da addebitare sul cliente"},"stripeAmount":{"type":"number","description":"Importo in valuta da pagare con Stripe. Se > 0 viene aperta una Checkout Session. walletUnits + stripeAmount deve essere uguale al prezzo della variante (`offerPrice`)."},"moneyAmount":{"type":"number","description":"Alias legacy di `stripeAmount` (stesso significato)."},"successUrl":{"type":"string","description":"URL redirect dopo pagamento Stripe"},"cancelUrl":{"type":"string","description":"URL redirect se pagamento Stripe annullato"},"captureMode":{"type":"string","enum":["automatic","manual"],"default":"automatic","description":"automatic = addebito immediato su Stripe; manual = autorizzazione/blocco fondi, capture al webhook dopo scalata wallet"},"present":{"type":"object","description":"Regalo opzionale per un terzo. Se `destEmailTo` o `destPhoneTo` sono valorizzati, l'acquisto è trattato come regalo: il destinatario riceve notifiche e il coupon/card.","properties":{"destEmailTo":{"type":"string","format":"email","description":"Email del destinatario (almeno uno tra email e telefono è obbligatorio per il regalo)"},"destPhoneTo":{"type":"string","description":"Telefono del destinatario con prefisso internazionale (es. +39...)"},"destNameTo":{"type":"string","description":"Nome del destinatario usato nei messaggi di notifica"},"senderName":{"type":"string","description":"Nome del mittente mostrato al destinatario"},"message":{"type":"string","description":"Messaggio personale incluso nelle notifiche"}},"example":{"destEmailTo":"r.may@keyx.it","destNameTo":"Riccardo May","message":"Buon compleanno!","senderName":"Rik May"}}},"example":{"variantId":20,"customerId":42,"paymentOptionId":30,"walletUnits":20,"stripeAmount":5,"present":{"destEmailTo":"r.may@keyx.it","destNameTo":"Riccardo May","message":"Buon compleanno!","senderName":"Rik May"}}}}},"required":true},"security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"in":"path","name":"programId","required":true},{"schema":{"type":"string"},"in":"path","name":"offerId","required":true}],"responses":{"200":{"description":"Acquisto coupon con moneyAmount > 0: sessione Stripe Checkout.","content":{"application/json":{"schema":{"description":"Acquisto coupon con moneyAmount > 0: sessione Stripe Checkout.","type":"object","additionalProperties":true,"example":{"purchasedId":8801,"status":"PENDING_PAYMENT","checkoutSessionId":"cs_test_a1b2c3","url":"https://checkout.stripe.com/c/pay/cs_test_a1b2c3","publishableKey":"pk_test_xxx","amount":1500,"currency":"eur","captureMode":"automatic","walletUnits":100,"moneyAmount":15}}}}},"201":{"description":"Acquisto coupon completato con wallet (status ISSUED). Supporta regalo opzionale.","content":{"application/json":{"schema":{"description":"Acquisto coupon completato con wallet (status ISSUED). Supporta regalo opzionale.","type":"object","additionalProperties":true,"example":{"id":88,"programId":1,"shopId":3,"offerId":10,"variantId":20,"customerId":42,"recipientCustomerId":null,"barcodeCode":"123456789012345678901234","status":"ISSUED","isGift":false,"giftSent":false,"giftReceived":false,"gift":null,"offerTitle":"Sconto 20% cena","variantDescription":"Cena per 2","shopName":"Ristorante Demo","purchasedAt":"2026-05-15T10:00:00.000Z","usedAt":null,"usedTerminalId":null,"blockedAt":null,"blockedReason":null}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/coupon-purchased/by-barcode/{barcodeCode}":{"get":{"tags":["CouponOffers"],"description":"Get purchased coupon details by barcode (read-only). Customer: own coupons only. POS: shop coupons only.\n\n**Contesto:** Le **Offerte coupon** sono deal vendibili (sconto cena, servizio) con varianti, slot di validità, pagamento wallet/denaro e integrazione Stripe.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n- `barcodeCode` (string) — obbligatorio: 24-digit barcode\n\n**Risposte di successo:**\n- **200**: Dettaglio coupon acquistato per barcode (solo lettura). Cliente: propri coupon; POS: coupon del negozio del terminale.\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** CouponOffers.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"string"},"in":"path","name":"barcodeCode","required":true,"description":"24-digit barcode"}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Dettaglio coupon acquistato per barcode (solo lettura). Cliente: propri coupon; POS: coupon del negozio del terminale.","content":{"application/json":{"schema":{"description":"Dettaglio coupon acquistato per barcode (solo lettura). Cliente: propri coupon; POS: coupon del negozio del terminale.","type":"object","additionalProperties":true,"example":{"id":88,"programId":1,"shopId":3,"offerId":10,"variantId":20,"customerId":42,"recipientCustomerId":null,"barcodeCode":"123456789012345678901234","status":"ISSUED","isGift":false,"giftSent":false,"giftReceived":false,"gift":null,"offerTitle":"Sconto 20% cena","variantDescription":"Cena per 2","shopName":"Ristorante Demo","purchasedAt":"2026-05-15T10:00:00.000Z","usedAt":null,"usedTerminalId":null,"blockedAt":null,"blockedReason":null}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/coupon-offers/redeem":{"post":{"tags":["CouponOffers"],"description":"Riscatta un coupon al POS tramite codice a barre (24 cifre). Crea movimento kind 68 con note coupon_by_user_id.\n\n**Contesto:** Le **Offerte coupon** sono deal vendibili (sconto cena, servizio) con varianti, slot di validità, pagamento wallet/denaro e integrazione Stripe.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (string) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Riscatta un coupon al POS tramite codice a barre (24 cifre). Crea movimento kind 68 con note coupon_by_user_id.\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** CouponOffers.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"in":"path","name":"programId","required":true}],"responses":{"200":{"description":"Riscatta un coupon al POS tramite codice a barre (24 cifre). Crea movimento kind 68 con note coupon_by_user_id.","content":{"application/json":{"schema":{"description":"Riscatta un coupon al POS tramite codice a barre (24 cifre). Crea movimento kind 68 con note coupon_by_user_id.","type":"object","additionalProperties":true,"example":{"id":88,"barcodeCode":"123456789012345678901234","status":"USED","offerTitle":"Sconto 20% cena","variantDescription":"Cena per 2","customerId":42,"movementId":5001}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/customers/{customerId}/coupon-purchased":{"get":{"tags":["CouponOffers"],"description":"Elenco paginato di coupon-purchased.\n\n**Contesto:** Le **Offerte coupon** sono deal vendibili (sconto cena, servizio) con varianti, slot di validità, pagamento wallet/denaro e integrazione Stripe.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `customerId` (string) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Coupon acquistati o ricevuti in regalo dal cliente (paginato).\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** CouponOffers.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"in":"path","name":"customerId","required":true}],"responses":{"200":{"description":"Coupon acquistati o ricevuti in regalo dal cliente (paginato).","content":{"application/json":{"schema":{"description":"Coupon acquistati o ricevuti in regalo dal cliente (paginato).","type":"object","additionalProperties":true,"example":{"content":[{"id":88,"programId":1,"shopId":3,"offerId":10,"variantId":20,"customerId":42,"recipientCustomerId":null,"barcodeCode":"123456789012345678901234","status":"ISSUED","isGift":false,"giftSent":false,"giftReceived":false,"gift":null,"offerTitle":"Sconto 20% cena","variantDescription":"Cena per 2","shopName":"Ristorante Demo","purchasedAt":"2026-05-15T10:00:00.000Z","usedAt":null,"usedTerminalId":null,"blockedAt":null,"blockedReason":null}],"totalElements":1,"page":1,"size":50}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/coupon-purchased/{id}/status":{"put":{"tags":["CouponOffers"],"description":"Aggiorna status (campi consentiti).\n\n**Contesto:** Le **Offerte coupon** sono deal vendibili (sconto cena, servizio) con varianti, slot di validità, pagamento wallet/denaro e integrazione Stripe.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `id` (string) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Imposta lo stato del coupon acquistato (ISSUED o BLOCKED).\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** CouponOffers.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Imposta lo stato del coupon acquistato (ISSUED o BLOCKED).","content":{"application/json":{"schema":{"description":"Imposta lo stato del coupon acquistato (ISSUED o BLOCKED).","type":"object","additionalProperties":true,"example":{"id":88,"programId":1,"shopId":3,"offerId":10,"variantId":20,"customerId":42,"recipientCustomerId":null,"barcodeCode":"123456789012345678901234","status":"ISSUED","isGift":false,"giftSent":false,"giftReceived":false,"gift":null,"offerTitle":"Sconto 20% cena","variantDescription":"Cena per 2","shopName":"Ristorante Demo","purchasedAt":"2026-05-15T10:00:00.000Z","usedAt":null,"usedTerminalId":null,"blockedAt":null,"blockedReason":null}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/coupon-stripe-payments":{"get":{"summary":"List Stripe coupon purchases (staff only; filters: customer, shop, date range)","tags":["CouponOffers"],"description":"Elenco paginato di coupon-stripe-payments del programma indicato in `programId`.\n\n**Contesto:** Le **Offerte coupon** sono deal vendibili (sconto cena, servizio) con varianti, slot di validità, pagamento wallet/denaro e integrazione Stripe.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n\n**Querystring:**\n- `customerId` (integer) — opzionale\n- `shopId` (integer) — opzionale\n- `dateFrom` (string) — opzionale\n- `dateTo` (string) — opzionale\n- `page` (integer) — opzionale [default: 1]\n- `size` (integer) — opzionale [default: 20]\n\n**Risposte di successo:**\n- **200**: Elenco pagamenti Stripe coupon del programma (solo staff).\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** CouponOffers.","parameters":[{"schema":{"type":"integer"},"in":"query","name":"customerId","required":false},{"schema":{"type":"integer"},"in":"query","name":"shopId","required":false},{"schema":{"type":"string","format":"date-time"},"in":"query","name":"dateFrom","required":false},{"schema":{"type":"string","format":"date-time"},"in":"query","name":"dateTo","required":false},{"schema":{"type":"integer","minimum":1,"default":1},"in":"query","name":"page","required":false},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":20},"in":"query","name":"size","required":false},{"schema":{"type":"integer"},"in":"path","name":"programId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Elenco pagamenti Stripe coupon del programma (solo staff).","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"description":"Elenco pagamenti Stripe coupon del programma (solo staff).","example":{"page":1,"size":20,"totalElements":1,"totalPages":1,"items":[{"id":8801,"customerId":200,"shopId":42,"shopName":"Negozio centro","offerId":15,"offerTitle":"Sconto 20%","variantId":3,"variantDescription":"1 coupon","barcodeCode":"CPN-ABC123","status":"ISSUED","moneyPaid":15,"stripePaymentIntentId":"pi_3Pxyz789","stripeCheckoutSessionId":"cs_test_a1b2c3","captureMode":"automatic","purchasedAt":"2026-05-15T10:00:00.000Z","usedAt":null}]}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/gift-cards":{"get":{"tags":["GiftCards"],"description":"Lista gift card del programma. Token CUSTOMER: solo card ENABLED con varianti ENABLED. Token POS: stessa lista del backoffice nel programma del terminale.\n\n**Contesto:** Le **Gift Card** sono prodotti prepagati con varianti (importo, wallet collegato). Acquistabili in APP/POS/Stripe e attivabili su carte cliente.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (string) — obbligatorio\n\n**Querystring:**\n- `status` (string) — opzionale (valori: ENABLED, DISABLED, TERMINATED)\n- `name` (string) — opzionale: Filtra per nome (ILIKE, case-insensitive)\n- `channel` (string) — opzionale: Filtra per tipologia da negozi associati (shops.kind): ONLINE/OFFLINE; program-wide compare in entrambi i filtri (valori: ONLINE, OFFLINE)\n- `shopId` (integer) — opzionale: Filtra card disponibili nel negozio\n- `page` (integer) — opzionale [default: 1]\n- `size` (integer) — opzionale [default: 50]\n\n**Risposte di successo:**\n- **200**: Lista paginata gift card del programma (admin). Ogni item include le varianti con statistiche vendite.\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** GiftCards.","parameters":[{"schema":{"type":"string","enum":["ENABLED","DISABLED","TERMINATED"]},"in":"query","name":"status","required":false},{"schema":{"type":"string"},"in":"query","name":"name","required":false,"description":"Filtra per nome (ILIKE, case-insensitive)"},{"schema":{"type":"string","enum":["ONLINE","OFFLINE"]},"in":"query","name":"channel","required":false,"description":"Filtra per tipologia da negozi associati (shops.kind): ONLINE/OFFLINE; program-wide compare in entrambi i filtri"},{"schema":{"type":"integer"},"in":"query","name":"shopId","required":false,"description":"Filtra card disponibili nel negozio"},{"schema":{"type":"integer","minimum":1,"default":1},"in":"query","name":"page","required":false},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":50},"in":"query","name":"size","required":false},{"schema":{"type":"string"},"in":"path","name":"programId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Lista paginata gift card del programma (admin). Ogni item include le varianti con statistiche vendite.","content":{"application/json":{"schema":{"description":"Lista paginata gift card del programma (admin). Ogni item include le varianti con statistiche vendite.","type":"object","additionalProperties":true,"example":{"page":1,"size":50,"totalElements":1,"totalPages":1,"items":[{"id":8,"programId":1,"ownerShopId":null,"ownerShopName":null,"name":"Gift Card 50€","foreignCatalogueId":"10340:lastminute.com - Gift Card","description":"Buono regalo spendibile nei negozi aderenti","terms":"Validità 12 mesi dalla data di acquisto.","conditions":{"spendMode":"street_and_online","spendSingle":false,"spendPartial":true},"tierId":6,"walletId":2,"posPurchaseEnabled":false,"status":"ENABLED","coverImageUrl":"https://cdn.example/gift-card-50.jpg","images":[{"id":10,"url":"https://cdn.example/gift-card-50.jpg","sortOrder":0}],"variants":[{"id":101,"giftCardId":8,"foreignVariantId":"863","cardValue":50,"purchasePrice":45,"currencyId":1,"currencyCode":"EUR","currencySymbol":"€","superDiscount":false,"status":"ENABLED","soldCount":12,"usedCount":3,"sortOrder":0,"freeAmount":false},{"id":102,"giftCardId":8,"foreignVariantId":null,"cardValue":null,"purchasePrice":null,"currencyId":1,"currencyCode":"EUR","currencySymbol":"€","superDiscount":false,"status":"ENABLED","soldCount":0,"usedCount":0,"sortOrder":1,"freeAmount":true}],"shopIds":[],"programWide":true,"hasSales":false,"locked":false,"createdAt":"2026-05-15T10:00:00.000Z","updatedAt":"2026-05-15T10:00:00.000Z"}]}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"post":{"tags":["GiftCards"],"description":"Crea una gift card. Le varianti si definiscono nel body `variants[]` (obbligatorio, minimo 1). Non esiste un endpoint dedicato alle varianti: si creano qui e si aggiornano con PUT.\n\n**Contesto:** Le **Gift Card** sono prodotti prepagati con varianti (importo, wallet collegato). Acquistabili in APP/POS/Stripe e attivabili su carte cliente.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (string) — obbligatorio\n\n**Body JSON:**\n- `name` (string) — obbligatorio: Nome gift card\n- `foreignCatalogueId` (string) — opzionale: ID catalogo BS/MDW: {merchant_id_bs}:{nome_producto} (es. \"10340:lastminute.com - Gift Card\")\n- `description` (string) — opzionale\n- `terms` (string) — opzionale: Termini e condizioni mostrati al cliente\n- `conditions` (object) — opzionale: Regole di spesa della card\n- `tierId` (integer) — opzionale: Tier assegnato alla card fisica creata in acquisto\n- `walletId` (integer) — opzionale: Wallet da cui prelevare i punti per l'acquisto GC (opzionale se acquisto solo Stripe o POS gratuito)\n- `posPurchaseEnabled` (boolean) — opzionale: Se true, il terminale POS può assegnare la GC al cliente senza addebito wallet né Stripe [default: false]\n- `status` (string) — opzionale (valori: ENABLED, DISABLED, TERMINATED)\n- `ownerShopId` (integer) — opzionale: Negozio creatore; null = livello programma\n- `images` (array) — opzionale: URL galleria immagini (ordine = sortOrder)\n- `shopIds` (array) — opzionale: Negozi abilitati alla vendita; array vuoto = tutto il programma\n- `variants` (array) — obbligatorio: Varianti prezzo/valore della gift card. Si creano e aggiornano solo tramite POST/PUT su questa risorsa (upsert: con `id` aggiorna, senza `id` crea; varianti assenti vengono rimosse se senza vendite). Si leggono in GET lista, dettaglio e catalogo.\n\n**Risposte di successo:**\n- **201**: Gift card creata. Le varianti vengono create dal body `variants[]` (almeno una obbligatoria). Per aggiungere o modificare varianti in seguito usare PUT con lo stesso array (upsert per id).\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** GiftCards.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["name","variants"],"properties":{"name":{"type":"string","description":"Nome gift card"},"foreignCatalogueId":{"type":"string","nullable":true,"description":"ID catalogo BS/MDW: {merchant_id_bs}:{nome_producto} (es. \"10340:lastminute.com - Gift Card\")"},"description":{"type":"string"},"terms":{"type":"string","description":"Termini e condizioni mostrati al cliente"},"conditions":{"type":"object","description":"Regole di spesa della card","properties":{"spendMode":{"type":"string","enum":["street","online","street_and_online"]},"spendSingle":{"type":"boolean","description":"Spesa in un'unica transazione"},"spendPartial":{"type":"boolean","description":"Consente spesa parziale del saldo"}}},"tierId":{"type":"integer","nullable":true,"description":"Tier assegnato alla card fisica creata in acquisto"},"walletId":{"type":"integer","nullable":true,"description":"Wallet da cui prelevare i punti per l'acquisto GC (opzionale se acquisto solo Stripe o POS gratuito)"},"posPurchaseEnabled":{"type":"boolean","default":false,"description":"Se true, il terminale POS può assegnare la GC al cliente senza addebito wallet né Stripe"},"status":{"type":"string","enum":["ENABLED","DISABLED","TERMINATED"]},"ownerShopId":{"type":"integer","nullable":true,"description":"Negozio creatore; null = livello programma"},"images":{"type":"array","items":{"type":"string","format":"uri"},"description":"URL galleria immagini (ordine = sortOrder)"},"shopIds":{"type":"array","items":{"type":"integer"},"description":"Negozi abilitati alla vendita; array vuoto = tutto il programma"},"variants":{"type":"array","minItems":1,"description":"Varianti prezzo/valore della gift card. Si creano e aggiornano solo tramite POST/PUT su questa risorsa (upsert: con `id` aggiorna, senza `id` crea; varianti assenti vengono rimosse se senza vendite). Si leggono in GET lista, dettaglio e catalogo.","items":{"type":"object","properties":{"id":{"type":"integer","description":"Presente in PUT per aggiornare una variante esistente"},"foreignVariantId":{"type":"string","nullable":true,"description":"ID variante nel catalogo BS (es. \"863\", \"864\")"},"cardValue":{"type":"number","nullable":true,"description":"Valore caricato sulla card; null insieme a purchasePrice = importo libero (freeAmount)"},"purchasePrice":{"type":"number","nullable":true,"description":"Prezzo pagato dal cliente; obbligatorio se cardValue è valorizzato"},"currencyId":{"type":"integer","description":"Default: valuta locale del programma"},"superDiscount":{"type":"boolean","description":"Prezzo promozionale (purchasePrice < cardValue)"},"status":{"type":"string","enum":["ENABLED","DISABLED","TERMINATED"]},"sortOrder":{"type":"integer"}}}}},"example":{"name":"lastminute.com - Gift Card","foreignCatalogueId":"10340:lastminute.com - Gift Card","description":"Buono regalo spendibile nei negozi aderenti","terms":"Validità 12 mesi.","conditions":{"spendMode":"online","spendSingle":true,"spendPartial":false},"tierId":12,"walletId":2674,"status":"ENABLED","images":["https://cdn.example/gift-card-50.jpg"],"shopIds":[1490],"variants":[{"foreignVariantId":"863","cardValue":50,"purchasePrice":44.6,"currencyId":1,"superDiscount":false,"status":"ENABLED","sortOrder":0},{"foreignVariantId":"864","cardValue":100,"purchasePrice":94.6,"currencyId":1,"superDiscount":false,"status":"ENABLED","sortOrder":1}]}}}},"required":true},"security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"in":"path","name":"programId","required":true}],"responses":{"201":{"description":"Gift card creata. Le varianti vengono create dal body `variants[]` (almeno una obbligatoria). Per aggiungere o modificare varianti in seguito usare PUT con lo stesso array (upsert per id).","content":{"application/json":{"schema":{"description":"Gift card creata. Le varianti vengono create dal body `variants[]` (almeno una obbligatoria). Per aggiungere o modificare varianti in seguito usare PUT con lo stesso array (upsert per id).","type":"object","additionalProperties":true,"example":{"id":8,"programId":1,"ownerShopId":null,"ownerShopName":null,"name":"Gift Card 50€","foreignCatalogueId":"10340:lastminute.com - Gift Card","description":"Buono regalo spendibile nei negozi aderenti","terms":"Validità 12 mesi dalla data di acquisto.","conditions":{"spendMode":"street_and_online","spendSingle":false,"spendPartial":true},"tierId":6,"walletId":2,"posPurchaseEnabled":false,"status":"ENABLED","coverImageUrl":"https://cdn.example/gift-card-50.jpg","images":[{"id":10,"url":"https://cdn.example/gift-card-50.jpg","sortOrder":0}],"variants":[{"id":101,"giftCardId":8,"foreignVariantId":"863","cardValue":50,"purchasePrice":45,"currencyId":1,"currencyCode":"EUR","currencySymbol":"€","superDiscount":false,"status":"ENABLED","soldCount":12,"usedCount":3,"sortOrder":0,"freeAmount":false},{"id":102,"giftCardId":8,"foreignVariantId":null,"cardValue":null,"purchasePrice":null,"currencyId":1,"currencyCode":"EUR","currencySymbol":"€","superDiscount":false,"status":"ENABLED","soldCount":0,"usedCount":0,"sortOrder":1,"freeAmount":true}],"shopIds":[],"programWide":true,"hasSales":false,"locked":false,"createdAt":"2026-05-15T10:00:00.000Z","updatedAt":"2026-05-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/gift-cards/catalog":{"get":{"tags":["GiftCards"],"description":"Catalogo gift card acquistabili dal cliente (token CUSTOMER consentito). Solo card ENABLED con almeno una variante ENABLED. Le varianti sono incluse in ogni item (non esiste endpoint separato).\n\n**Contesto:** Le **Gift Card** sono prodotti prepagati con varianti (importo, wallet collegato). Acquistabili in APP/POS/Stripe e attivabili su carte cliente.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (string) — obbligatorio\n\n**Querystring:**\n- `shopId` (integer) — opzionale: Filtra per negozio (card program-wide o assegnate al negozio)\n\n**Risposte di successo:**\n- **200**: Catalogo gift card acquistabili dal cliente (app/POS). Restituisce solo card ENABLED con almeno una variante ENABLED. Opzionale `shopId` per filtrare per negozio. Le varianti sono incluse in ogni item (non c'è endpoint separato).\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** GiftCards.","parameters":[{"schema":{"type":"integer"},"in":"query","name":"shopId","required":false,"description":"Filtra per negozio (card program-wide o assegnate al negozio)"},{"schema":{"type":"string"},"in":"path","name":"programId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Catalogo gift card acquistabili dal cliente (app/POS). Restituisce solo card ENABLED con almeno una variante ENABLED. Opzionale `shopId` per filtrare per negozio. Le varianti sono incluse in ogni item (non c'è endpoint separato).","content":{"application/json":{"schema":{"description":"Catalogo gift card acquistabili dal cliente (app/POS). Restituisce solo card ENABLED con almeno una variante ENABLED. Opzionale `shopId` per filtrare per negozio. Le varianti sono incluse in ogni item (non c'è endpoint separato).","type":"object","additionalProperties":true,"example":{"items":[{"id":8,"programId":1,"ownerShopId":null,"ownerShopName":null,"name":"Gift Card 50€","foreignCatalogueId":"10340:lastminute.com - Gift Card","description":"Buono regalo spendibile nei negozi aderenti","terms":"Validità 12 mesi dalla data di acquisto.","conditions":{"spendMode":"street_and_online","spendSingle":false,"spendPartial":true},"tierId":6,"walletId":2,"posPurchaseEnabled":false,"status":"ENABLED","coverImageUrl":"https://cdn.example/gift-card-50.jpg","images":[{"id":10,"url":"https://cdn.example/gift-card-50.jpg","sortOrder":0}],"variants":[{"id":101,"giftCardId":8,"foreignVariantId":"863","cardValue":50,"purchasePrice":45,"currencyId":1,"currencyCode":"EUR","currencySymbol":"€","superDiscount":false,"status":"ENABLED","soldCount":12,"usedCount":3,"sortOrder":0,"freeAmount":false}],"shopIds":[],"programWide":true,"hasSales":false,"locked":false,"createdAt":"2026-05-15T10:00:00.000Z","updatedAt":"2026-05-15T10:00:00.000Z"}]}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/gift-cards/{id}":{"get":{"tags":["GiftCards"],"description":"Dettaglio gift card con immagini, varianti e negozi assegnati. Con token CUSTOMER: solo card ENABLED e varianti ENABLED (come il catalogo).\n\n**Contesto:** Le **Gift Card** sono prodotti prepagati con varianti (importo, wallet collegato). Acquistabili in APP/POS/Stripe e attivabili su carte cliente.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (string) — obbligatorio\n- `id` (string) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Dettaglio gift card con immagini, varianti e negozi assegnati.\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** GiftCards.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"in":"path","name":"programId","required":true},{"schema":{"type":"string"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Dettaglio gift card con immagini, varianti e negozi assegnati.","content":{"application/json":{"schema":{"description":"Dettaglio gift card con immagini, varianti e negozi assegnati.","type":"object","additionalProperties":true,"example":{"id":8,"programId":1,"ownerShopId":null,"ownerShopName":null,"name":"Gift Card 50€","foreignCatalogueId":"10340:lastminute.com - Gift Card","description":"Buono regalo spendibile nei negozi aderenti","terms":"Validità 12 mesi dalla data di acquisto.","conditions":{"spendMode":"street_and_online","spendSingle":false,"spendPartial":true},"tierId":6,"walletId":2,"posPurchaseEnabled":false,"status":"ENABLED","coverImageUrl":"https://cdn.example/gift-card-50.jpg","images":[{"id":10,"url":"https://cdn.example/gift-card-50.jpg","sortOrder":0}],"variants":[{"id":101,"giftCardId":8,"foreignVariantId":"863","cardValue":50,"purchasePrice":45,"currencyId":1,"currencyCode":"EUR","currencySymbol":"€","superDiscount":false,"status":"ENABLED","soldCount":12,"usedCount":3,"sortOrder":0,"freeAmount":false},{"id":102,"giftCardId":8,"foreignVariantId":null,"cardValue":null,"purchasePrice":null,"currencyId":1,"currencyCode":"EUR","currencySymbol":"€","superDiscount":false,"status":"ENABLED","soldCount":0,"usedCount":0,"sortOrder":1,"freeAmount":true}],"shopIds":[],"programWide":true,"hasSales":false,"locked":false,"createdAt":"2026-05-15T10:00:00.000Z","updatedAt":"2026-05-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"put":{"tags":["GiftCards"],"description":"Aggiorna gift card e varianti (upsert: varianti con `id` aggiornate, senza `id` create, assenti rimosse se senza vendite). Con vendite esistenti (`locked`) si aggiornano `status`, `shopIds` e il `purchasePrice` delle varianti esistenti.\n\n**Contesto:** Le **Gift Card** sono prodotti prepagati con varianti (importo, wallet collegato). Acquistabili in APP/POS/Stripe e attivabili su carte cliente.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (string) — obbligatorio\n- `id` (string) — obbligatorio\n\n**Body JSON:**\n- `name` (string) — obbligatorio: Nome gift card\n- `foreignCatalogueId` (string) — opzionale: ID catalogo BS/MDW: {merchant_id_bs}:{nome_producto} (es. \"10340:lastminute.com - Gift Card\")\n- `description` (string) — opzionale\n- `terms` (string) — opzionale: Termini e condizioni mostrati al cliente\n- `conditions` (object) — opzionale: Regole di spesa della card\n- `tierId` (integer) — opzionale: Tier assegnato alla card fisica creata in acquisto\n- `walletId` (integer) — opzionale: Wallet da cui prelevare i punti per l'acquisto GC (opzionale se acquisto solo Stripe o POS gratuito)\n- `posPurchaseEnabled` (boolean) — opzionale: Se true, il terminale POS può assegnare la GC al cliente senza addebito wallet né Stripe [default: false]\n- `status` (string) — opzionale (valori: ENABLED, DISABLED, TERMINATED)\n- `ownerShopId` (integer) — opzionale: Negozio creatore; null = livello programma\n- `images` (array) — opzionale: URL galleria immagini (ordine = sortOrder)\n- `shopIds` (array) — opzionale: Negozi abilitati alla vendita; array vuoto = tutto il programma\n- `variants` (array) — obbligatorio: Varianti prezzo/valore della gift card. Si creano e aggiornano solo tramite POST/PUT su questa risorsa (upsert: con `id` aggiorna, senza `id` crea; varianti assenti vengono rimosse se senza vendite). Si leggono in GET lista, dettaglio e catalogo.\n\n**Risposte di successo:**\n- **200**: Gift card aggiornata. Se esistono vendite (`locked: true`) si aggiornano `status`, `shopIds` e il `purchasePrice` delle varianti esistenti; altrimenti anche varianti e campi descrittivi.\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** GiftCards.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["name","variants"],"properties":{"name":{"type":"string","description":"Nome gift card"},"foreignCatalogueId":{"type":"string","nullable":true,"description":"ID catalogo BS/MDW: {merchant_id_bs}:{nome_producto} (es. \"10340:lastminute.com - Gift Card\")"},"description":{"type":"string"},"terms":{"type":"string","description":"Termini e condizioni mostrati al cliente"},"conditions":{"type":"object","description":"Regole di spesa della card","properties":{"spendMode":{"type":"string","enum":["street","online","street_and_online"]},"spendSingle":{"type":"boolean","description":"Spesa in un'unica transazione"},"spendPartial":{"type":"boolean","description":"Consente spesa parziale del saldo"}}},"tierId":{"type":"integer","nullable":true,"description":"Tier assegnato alla card fisica creata in acquisto"},"walletId":{"type":"integer","nullable":true,"description":"Wallet da cui prelevare i punti per l'acquisto GC (opzionale se acquisto solo Stripe o POS gratuito)"},"posPurchaseEnabled":{"type":"boolean","default":false,"description":"Se true, il terminale POS può assegnare la GC al cliente senza addebito wallet né Stripe"},"status":{"type":"string","enum":["ENABLED","DISABLED","TERMINATED"]},"ownerShopId":{"type":"integer","nullable":true,"description":"Negozio creatore; null = livello programma"},"images":{"type":"array","items":{"type":"string","format":"uri"},"description":"URL galleria immagini (ordine = sortOrder)"},"shopIds":{"type":"array","items":{"type":"integer"},"description":"Negozi abilitati alla vendita; array vuoto = tutto il programma"},"variants":{"type":"array","minItems":1,"description":"Varianti prezzo/valore della gift card. Si creano e aggiornano solo tramite POST/PUT su questa risorsa (upsert: con `id` aggiorna, senza `id` crea; varianti assenti vengono rimosse se senza vendite). Si leggono in GET lista, dettaglio e catalogo.","items":{"type":"object","properties":{"id":{"type":"integer","description":"Presente in PUT per aggiornare una variante esistente"},"foreignVariantId":{"type":"string","nullable":true,"description":"ID variante nel catalogo BS (es. \"863\", \"864\")"},"cardValue":{"type":"number","nullable":true,"description":"Valore caricato sulla card; null insieme a purchasePrice = importo libero (freeAmount)"},"purchasePrice":{"type":"number","nullable":true,"description":"Prezzo pagato dal cliente; obbligatorio se cardValue è valorizzato"},"currencyId":{"type":"integer","description":"Default: valuta locale del programma"},"superDiscount":{"type":"boolean","description":"Prezzo promozionale (purchasePrice < cardValue)"},"status":{"type":"string","enum":["ENABLED","DISABLED","TERMINATED"]},"sortOrder":{"type":"integer"}}}}},"example":{"name":"lastminute.com - Gift Card","foreignCatalogueId":"10340:lastminute.com - Gift Card","description":"Buono regalo spendibile nei negozi aderenti","terms":"Validità 12 mesi.","conditions":{"spendMode":"online","spendSingle":true,"spendPartial":false},"tierId":12,"walletId":2674,"status":"ENABLED","images":["https://cdn.example/gift-card-50.jpg"],"shopIds":[1490],"variants":[{"foreignVariantId":"863","cardValue":50,"purchasePrice":44.6,"currencyId":1,"superDiscount":false,"status":"ENABLED","sortOrder":0},{"foreignVariantId":"864","cardValue":100,"purchasePrice":94.6,"currencyId":1,"superDiscount":false,"status":"ENABLED","sortOrder":1}]}}}},"required":true},"security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"in":"path","name":"programId","required":true},{"schema":{"type":"string"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Gift card aggiornata. Se esistono vendite (`locked: true`) si aggiornano `status`, `shopIds` e il `purchasePrice` delle varianti esistenti; altrimenti anche varianti e campi descrittivi.","content":{"application/json":{"schema":{"description":"Gift card aggiornata. Se esistono vendite (`locked: true`) si aggiornano `status`, `shopIds` e il `purchasePrice` delle varianti esistenti; altrimenti anche varianti e campi descrittivi.","type":"object","additionalProperties":true,"example":{"id":8,"programId":1,"ownerShopId":null,"ownerShopName":null,"name":"Gift Card 50€","foreignCatalogueId":"10340:lastminute.com - Gift Card","description":"Buono regalo spendibile nei negozi aderenti","terms":"Validità 12 mesi dalla data di acquisto.","conditions":{"spendMode":"street_and_online","spendSingle":false,"spendPartial":true},"tierId":6,"walletId":2,"posPurchaseEnabled":false,"status":"ENABLED","coverImageUrl":"https://cdn.example/gift-card-50.jpg","images":[{"id":10,"url":"https://cdn.example/gift-card-50.jpg","sortOrder":0}],"variants":[{"id":101,"giftCardId":8,"foreignVariantId":"863","cardValue":50,"purchasePrice":45,"currencyId":1,"currencyCode":"EUR","currencySymbol":"€","superDiscount":false,"status":"ENABLED","soldCount":12,"usedCount":3,"sortOrder":0,"freeAmount":false},{"id":102,"giftCardId":8,"foreignVariantId":null,"cardValue":null,"purchasePrice":null,"currencyId":1,"currencyCode":"EUR","currencySymbol":"€","superDiscount":false,"status":"ENABLED","soldCount":0,"usedCount":0,"sortOrder":1,"freeAmount":true}],"shopIds":[],"programWide":true,"hasSales":true,"locked":true,"createdAt":"2026-05-15T10:00:00.000Z","updatedAt":"2026-05-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"delete":{"tags":["GiftCards"],"description":"Termina ed elimina logicamente una gift card del programma (status TERMINATED, soft-delete).\n\n**Contesto:** Le **Gift Card** sono prodotti prepagati con varianti (importo, wallet collegato). Acquistabili in APP/POS/Stripe e attivabili su carte cliente.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (string) — obbligatorio\n- `id` (string) — obbligatorio\n\n**Risposte di successo:**\n- **204**: Gift card terminata (nessun corpo).\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** GiftCards.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"in":"path","name":"programId","required":true},{"schema":{"type":"string"},"in":"path","name":"id","required":true}],"responses":{"204":{"description":"Gift card terminata (nessun corpo)."},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/gift-cards/{id}/shops":{"put":{"tags":["GiftCards"],"description":"Aggiorna una programma fedeltà (campi consentiti) del programma indicato in `programId`.\n\n**Contesto:** Le **Gift Card** sono prodotti prepagati con varianti (importo, wallet collegato). Acquistabili in APP/POS/Stripe e attivabili su carte cliente.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (string) — obbligatorio\n- `id` (string) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Gift card aggiornata. Se esistono vendite (`locked: true`) si aggiornano `status`, `shopIds` e il `purchasePrice` delle varianti esistenti; altrimenti anche varianti e campi descrittivi.\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** GiftCards.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"in":"path","name":"programId","required":true},{"schema":{"type":"string"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Gift card aggiornata. Se esistono vendite (`locked: true`) si aggiornano `status`, `shopIds` e il `purchasePrice` delle varianti esistenti; altrimenti anche varianti e campi descrittivi.","content":{"application/json":{"schema":{"description":"Gift card aggiornata. Se esistono vendite (`locked: true`) si aggiornano `status`, `shopIds` e il `purchasePrice` delle varianti esistenti; altrimenti anche varianti e campi descrittivi.","type":"object","additionalProperties":true,"example":{"id":8,"programId":1,"ownerShopId":null,"ownerShopName":null,"name":"Gift Card 50€","foreignCatalogueId":"10340:lastminute.com - Gift Card","description":"Buono regalo spendibile nei negozi aderenti","terms":"Validità 12 mesi dalla data di acquisto.","conditions":{"spendMode":"street_and_online","spendSingle":false,"spendPartial":true},"tierId":6,"walletId":2,"posPurchaseEnabled":false,"status":"ENABLED","coverImageUrl":"https://cdn.example/gift-card-50.jpg","images":[{"id":10,"url":"https://cdn.example/gift-card-50.jpg","sortOrder":0}],"variants":[{"id":101,"giftCardId":8,"foreignVariantId":"863","cardValue":50,"purchasePrice":45,"currencyId":1,"currencyCode":"EUR","currencySymbol":"€","superDiscount":false,"status":"ENABLED","soldCount":12,"usedCount":3,"sortOrder":0,"freeAmount":false},{"id":102,"giftCardId":8,"foreignVariantId":null,"cardValue":null,"purchasePrice":null,"currencyId":1,"currencyCode":"EUR","currencySymbol":"€","superDiscount":false,"status":"ENABLED","soldCount":0,"usedCount":0,"sortOrder":1,"freeAmount":true}],"shopIds":[],"programWide":true,"hasSales":true,"locked":true,"createdAt":"2026-05-15T10:00:00.000Z","updatedAt":"2026-05-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/gift-cards/{id}/purchased":{"get":{"tags":["GiftCards"],"description":"Elenco paginato degli acquisti effettuati per una gift card. Token CUSTOMER: solo acquisti del sub JWT (`customer_id`). Token POS/backoffice: tutti gli acquisti della card.\n\n**Contesto:** Le **Gift Card** sono prodotti prepagati con varianti (importo, wallet collegato). Acquistabili in APP/POS/Stripe e attivabili su carte cliente.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (string) — obbligatorio\n- `id` (string) — obbligatorio\n\n**Querystring:**\n- `variantId` (integer) — opzionale: Filtra per variante\n- `status` (string) — opzionale: Stato acquisto (valori: PENDING_PAYMENT, ISSUED, USED, EXPIRED, TERMINATED)\n- `page` (integer) — opzionale [default: 1]\n- `size` (integer) — opzionale [default: 20]\n\n**Risposte di successo:**\n- **200**: Acquisti effettuati per una gift card (admin/backoffice). Paginato; filtri opzionali `variantId` e `status`. `cardPngUrl` e `cardPdfUrl` valorizzati solo con token CUSTOMER acquirente/destinatario della riga.\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** GiftCards.","parameters":[{"schema":{"type":"integer"},"in":"query","name":"variantId","required":false,"description":"Filtra per variante"},{"schema":{"type":"string","enum":["PENDING_PAYMENT","ISSUED","USED","EXPIRED","TERMINATED"]},"in":"query","name":"status","required":false,"description":"Stato acquisto"},{"schema":{"type":"integer","minimum":1,"default":1},"in":"query","name":"page","required":false},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":20},"in":"query","name":"size","required":false},{"schema":{"type":"string"},"in":"path","name":"programId","required":true},{"schema":{"type":"string"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Acquisti effettuati per una gift card (admin/backoffice). Paginato; filtri opzionali `variantId` e `status`. `cardPngUrl` e `cardPdfUrl` valorizzati solo con token CUSTOMER acquirente/destinatario della riga.","content":{"application/json":{"schema":{"description":"Acquisti effettuati per una gift card (admin/backoffice). Paginato; filtri opzionali `variantId` e `status`. `cardPngUrl` e `cardPdfUrl` valorizzati solo con token CUSTOMER acquirente/destinatario della riga.","type":"object","additionalProperties":true,"example":{"page":1,"size":20,"totalElements":1,"items":[{"id":501,"programId":1,"giftCardId":8,"variantId":101,"customerId":42,"purchaseShopId":3,"cardCode":"GC12XXXXXXXXXX3092","cardCodeRevealed":false,"cardValue":50,"purchasePrice":45,"currencyId":1,"remainingValue":50,"status":"ISSUED","fulfillmentSource":"BESTSHOPPING","bsOrderId":"bs-ord-20260605-004821","bsOrderStatus":"CONFIRMED","cardPngUrl":null,"cardPdfUrl":null,"walletMovementId":null,"walletUnitsPaid":null,"moneyPaid":45,"stripePaymentIntentId":"pi_3Pabc123","isGift":false,"purchasedAt":"2026-05-15T10:00:00.000Z","usedAt":null}]}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/gift-cards/{giftCardId}/variants/{variantId}/purchase":{"post":{"tags":["GiftCards"],"description":"Acquista una variante gift card. CUSTOMER: customerId dal JWT. POS: se `customerId` è omesso o 0, crea un cliente anonimo per GC regalo; altrimenti acquisto sul cliente indicato. Emissione gratuita solo se `posPurchaseEnabled` sulla card; per varianti importo libero inviare `cardValue` (o alias `amount` / `initialamount`). Passare walletUnits e/o stripeAmount per pagamento wallet/Stripe.\n\n**Contesto:** Le **Gift Card** sono prodotti prepagati con varianti (importo, wallet collegato). Acquistabili in APP/POS/Stripe e attivabili su carte cliente.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (string) — obbligatorio\n- `giftCardId` (string) — obbligatorio\n- `variantId` (string) — obbligatorio\n\n**Body JSON:**\n- `customerId` (integer) — opzionale: POS/backoffice: cliente acquirente; se omesso o 0 al POS, viene creato un cliente anonimo per GC regalo. Ignorato se token CUSTOMER.\n- `purchaseShopId` (integer) — opzionale: Negozio contesto acquisto (POS: default shopId del terminale)\n- `cardValue` (number) — opzionale: Obbligatorio per varianti importo libero (`freeAmount`, `card_value` null in DB). Alias accettati: `card_value`, `amount`, `initialamount`, `purchasePrice`. Con token POS definisce il valore caricato sulla GC.\n- `walletUnits` (number) — opzionale: Unità wallet da addebitare sul cliente. walletUnits + stripeAmount deve essere uguale al prezzo di acquisto.\n- `stripeAmount` (number) — opzionale: Importo in valuta da pagare con Stripe. Se > 0 viene aperta una Checkout Session.\n- `moneyAmount` (number) — opzionale: Alias legacy di `stripeAmount` (stesso significato).\n- `successUrl` (string) — opzionale: URL redirect dopo pagamento Stripe\n- `cancelUrl` (string) — opzionale: URL redirect se pagamento Stripe annullato\n- `captureMode` (string) — opzionale: automatic = addebito immediato su Stripe; manual = autorizzazione/blocco fondi, capture al webhook dopo scalata wallet (valori: automatic, manual) [default: \"automatic\"]\n- `present` (object) — opzionale: Regalo opzionale per un terzo. Se `destEmailTo` o `destPhoneTo` sono valorizzati, l'acquisto è trattato come regalo: il destinatario riceve notifiche e il coupon/card.\n\n**Risposte di successo:**\n- **200**: Acquisto gift card con moneyAmount > 0: sessione Stripe Checkout da completare (status PENDING_PAYMENT).\n- **201**: Acquisto gift card completato con wallet (status ISSUED).\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** GiftCards.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"customerId":{"type":"integer","description":"POS/backoffice: cliente acquirente; se omesso o 0 al POS, viene creato un cliente anonimo per GC regalo. Ignorato se token CUSTOMER."},"purchaseShopId":{"type":"integer","description":"Negozio contesto acquisto (POS: default shopId del terminale)"},"cardValue":{"type":"number","description":"Obbligatorio per varianti importo libero (`freeAmount`, `card_value` null in DB). Alias accettati: `card_value`, `amount`, `initialamount`, `purchasePrice`. Con token POS definisce il valore caricato sulla GC."},"walletUnits":{"type":"number","description":"Unità wallet da addebitare sul cliente. walletUnits + stripeAmount deve essere uguale al prezzo di acquisto."},"stripeAmount":{"type":"number","description":"Importo in valuta da pagare con Stripe. Se > 0 viene aperta una Checkout Session."},"moneyAmount":{"type":"number","description":"Alias legacy di `stripeAmount` (stesso significato)."},"successUrl":{"type":"string","description":"URL redirect dopo pagamento Stripe"},"cancelUrl":{"type":"string","description":"URL redirect se pagamento Stripe annullato"},"captureMode":{"type":"string","enum":["automatic","manual"],"default":"automatic","description":"automatic = addebito immediato su Stripe; manual = autorizzazione/blocco fondi, capture al webhook dopo scalata wallet"},"present":{"type":"object","description":"Regalo opzionale per un terzo. Se `destEmailTo` o `destPhoneTo` sono valorizzati, l'acquisto è trattato come regalo: il destinatario riceve notifiche e il coupon/card.","properties":{"destEmailTo":{"type":"string","format":"email","description":"Email del destinatario (almeno uno tra email e telefono è obbligatorio per il regalo)"},"destPhoneTo":{"type":"string","description":"Telefono del destinatario con prefisso internazionale (es. +39...)"},"destNameTo":{"type":"string","description":"Nome del destinatario usato nei messaggi di notifica"},"senderName":{"type":"string","description":"Nome del mittente mostrato al destinatario"},"message":{"type":"string","description":"Messaggio personale incluso nelle notifiche"}},"example":{"destEmailTo":"r.may@keyx.it","destNameTo":"Riccardo May","message":"Buon compleanno!","senderName":"Rik May"}}},"example":{"customerId":42,"purchaseShopId":3,"cardValue":75,"walletUnits":20,"stripeAmount":25,"moneyAmount":25,"successUrl":"https://app.example.com/gift-cards/success","cancelUrl":"https://app.example.com/gift-cards/cancel","captureMode":"automatic","present":{"destEmailTo":"r.may@keyx.it","destNameTo":"Riccardo May","message":"Buon compleanno!","senderName":"Rik May","destPhoneTo":"+393331234567"}}}}}},"security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"in":"path","name":"programId","required":true},{"schema":{"type":"string"},"in":"path","name":"giftCardId","required":true},{"schema":{"type":"string"},"in":"path","name":"variantId","required":true}],"responses":{"200":{"description":"Acquisto gift card con moneyAmount > 0: sessione Stripe Checkout da completare (status PENDING_PAYMENT).","content":{"application/json":{"schema":{"description":"Acquisto gift card con moneyAmount > 0: sessione Stripe Checkout da completare (status PENDING_PAYMENT).","type":"object","additionalProperties":true,"example":{"purchasedId":501,"status":"PENDING_PAYMENT","checkoutSessionId":"cs_test_a1b2c3","url":"https://checkout.stripe.com/c/pay/cs_test_a1b2c3","publishableKey":"pk_test_xxx","amount":2500,"currency":"eur","captureMode":"automatic","walletUnits":20,"moneyAmount":25}}}}},"201":{"description":"Acquisto gift card completato con wallet (status ISSUED).","content":{"application/json":{"schema":{"description":"Acquisto gift card completato con wallet (status ISSUED).","type":"object","additionalProperties":true,"example":{"id":501,"programId":1,"giftCardId":8,"variantId":101,"customerId":42,"purchaseShopId":3,"cardCode":"GC12XXXXXXXXXX3092","cardCodeRevealed":false,"cardValue":50,"purchasePrice":45,"currencyId":1,"remainingValue":50,"status":"ISSUED","fulfillmentSource":"BESTSHOPPING","bsOrderId":"bs-ord-20260605-004821","bsOrderStatus":"CONFIRMED","cardPngUrl":null,"cardPdfUrl":null,"walletMovementId":null,"walletUnitsPaid":null,"moneyPaid":45,"stripePaymentIntentId":"pi_3Pabc123","isGift":false,"purchasedAt":"2026-05-15T10:00:00.000Z","usedAt":null}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/customers/{customerId}/gift-card-purchased":{"get":{"tags":["GiftCards"],"description":"Gift card acquistate o ricevute in regalo dal cliente (acquirente o destinatario). Token CUSTOMER: solo se :customerId coincide con il sub del JWT. Esclude PENDING_PAYMENT e TERMINATED. `cardPngUrl` e `cardPdfUrl` solo con token CUSTOMER e :customerId = sub JWT.\n\n**Contesto:** Le **Gift Card** sono prodotti prepagati con varianti (importo, wallet collegato). Acquistabili in APP/POS/Stripe e attivabili su carte cliente.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `customerId` (string) — obbligatorio\n\n**Querystring:**\n- `status` (string) — opzionale: Filtra per stato (valori: ISSUED, USED, EXPIRED)\n- `page` (integer) — opzionale [default: 1]\n- `size` (integer) — opzionale [default: 20]\n\n**Risposte di successo:**\n- **200**: Gift card acquistate o ricevute in regalo dal cliente (acquirente o destinatario). Esclude stati PENDING_PAYMENT e TERMINATED. Ogni item include `giftCardName`. `cardPngUrl` e `cardPdfUrl` solo con token CUSTOMER e `:customerId` = sub JWT.\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** GiftCards.","parameters":[{"schema":{"type":"string"},"in":"query","name":"status","required":false,"description":"Filtro stato: valore singolo o CSV (es. CUSED,USED). Valori: ISSUED, PARTIALLY_USED, CUSED, USED, EXPIRED, …"},{"schema":{"type":"integer","minimum":1,"default":1},"in":"query","name":"page","required":false},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":20},"in":"query","name":"size","required":false},{"schema":{"type":"string"},"in":"path","name":"customerId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Gift card acquistate o ricevute in regalo dal cliente (acquirente o destinatario). Esclude stati PENDING_PAYMENT e TERMINATED. Ogni item include `giftCardName`. `cardPngUrl` e `cardPdfUrl` solo con token CUSTOMER e `:customerId` = sub JWT.","content":{"application/json":{"schema":{"description":"Gift card acquistate o ricevute in regalo dal cliente (acquirente o destinatario). Esclude stati PENDING_PAYMENT e TERMINATED. Ogni item include `giftCardName`. `cardPngUrl` e `cardPdfUrl` solo con token CUSTOMER e `:customerId` = sub JWT.","type":"object","additionalProperties":true,"example":{"page":1,"size":20,"totalElements":1,"content":[{"id":501,"programId":1,"giftCardId":8,"variantId":101,"customerId":42,"purchaseShopId":3,"cardCode":"BS3478208","cardCodeRevealed":true,"cardValue":50,"purchasePrice":45,"currencyId":1,"remainingValue":50,"status":"ISSUED","fulfillmentSource":"BESTSHOPPING","bsOrderId":"bs-ord-20260605-004821","bsOrderStatus":"CONFIRMED","cardPngUrl":"https://cdn.example.com/programs/5/GiftCards/501-voucher.png","cardPdfUrl":"https://cdn.example.com/programs/5/GiftCards/501-voucher.pdf","walletMovementId":null,"walletUnitsPaid":null,"moneyPaid":45,"stripePaymentIntentId":"pi_3Pabc123","isGift":false,"purchasedAt":"2026-05-15T10:00:00.000Z","usedAt":null,"giftCardName":"Gift Card 50€","giftCardTitle":"Gift Card 50€"}]}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/customers/{customerId}/gift-card-purchased/{id}":{"patch":{"tags":["GiftCards"],"description":"Sposta una gift card del cliente tra Disponibili (ISSUED) e Utilizzate (CUSED). Non cambia lo stato di spesa reale (USED).","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["status"],"properties":{"status":{"type":"string","enum":["ISSUED","CUSED"]}}}}},"required":true},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"customerId","required":true},{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Risorsa aggiornata (PATCH)","content":{"application/json":{"schema":{"description":"Risorsa aggiornata (PATCH)","type":"object","additionalProperties":true,"example":{"id":1,"updated":true}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"delete":{"tags":["GiftCards"],"description":"Soft-delete (TERMINATED) di una gift card posseduta dal cliente.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"customerId","required":true},{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"204":{"description":"Operazione riuscita senza corpo di risposta."},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/customers/{customerId}/gifts":{"get":{"tags":["Customers"],"description":"Regali fatti e/o ricevuti dal cliente (coupon e gift card con is_gift=true). direction=sent: acquisti regalo effettuati; direction=received: regali accreditati al cliente; kind=COUPON|GIFT_CARD|ALL.\n\n**Contesto:** I **Clienti** sono gli iscritti al programma fedeltà: anagrafica, tier, carte, saldi wallet, consensi, promo vinte e storico movimenti.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `customerId` (integer) — obbligatorio\n\n**Querystring:**\n- `kind` (string) — opzionale: Tipo regalo (valori: COUPON, GIFT_CARD, ALL) [default: \"ALL\"]\n- `direction` (string) — opzionale: sent = regalati ad altri; received = ricevuti in regalo (valori: sent, received, all) [default: \"all\"]\n- `status` (string) — opzionale: Coupon: disponibile|utilizzato|bloccato|scaduto. Gift card: ISSUED|USED|EXPIRED|…\n- `page` (integer) — opzionale [default: 1]\n- `size` (integer) — opzionale [default: 50]\n\n**Risposte di successo:**\n- **200**: Default Response\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Customers.","parameters":[{"schema":{"type":"string","enum":["COUPON","GIFT_CARD","ALL"],"default":"ALL"},"in":"query","name":"kind","required":false,"description":"Tipo regalo"},{"schema":{"type":"string","default":"all"},"in":"query","name":"direction","required":false,"description":"sent | received | all (case-insensitive)"},{"schema":{"type":"string"},"in":"query","name":"usageStatus","required":false,"description":"AVAILABLE|USED — se presente, filtra solo received non nascosti"},{"schema":{"type":"string"},"in":"query","name":"status","required":false,"description":"Stato oggetto acquistato (coupon/gift card)."},{"schema":{"type":"integer","minimum":1,"default":1},"in":"query","name":"page","required":false},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":50},"in":"query","name":"size","required":false},{"schema":{"type":"integer"},"in":"path","name":"customerId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"content":{"type":"array","items":{"type":"object","additionalProperties":true}},"totalElements":{"type":"integer"},"page":{"type":"integer"},"size":{"type":"integer"}},"example":{"content":[],"totalElements":0,"page":0,"size":0}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/customers/{customerId}/gifts/{presentId}":{"patch":{"tags":["Customers"],"description":"Aggiorna usageStatus del regalo (solo destinatario): AVAILABLE o USED.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["status"],"properties":{"status":{"type":"string","enum":["AVAILABLE","USED"]}}}}},"required":true},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"customerId","required":true},{"schema":{"type":"integer"},"in":"path","name":"presentId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"delete":{"tags":["Customers"],"description":"Nasconde il regalo dal lato del caller (mittente o destinatario).","parameters":[{"schema":{"type":"integer"},"in":"path","name":"customerId","required":true},{"schema":{"type":"integer"},"in":"path","name":"presentId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/stripe-payments":{"get":{"tags":["Stripe"],"description":"Elenco paginato di stripe-payments del programma indicato in `programId`.\n\n**Contesto:** Integrazione **Stripe** per pagamenti online (premi, coupon, gift card): config chiavi dual-mode (test/live), PaymentIntent, webhook ed eventi.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n\n**Querystring:**\n- `purchaseType` (string) — opzionale (valori: PRIZE, COUPON, GIFT_CARD)\n- `customerId` (integer) — opzionale\n- `shopId` (integer) — opzionale\n- `dateFrom` (string) — opzionale\n- `dateTo` (string) — opzionale\n- `page` (integer) — opzionale [default: 1]\n- `size` (integer) — opzionale [default: 20]\n\n**Risposte di successo:**\n- **200**: Pagamenti Stripe del programma (Premio, Coupon, Gift Card). Ogni item descrive l'oggetto acquistato (`itemTitle`, `itemSubtitle`), l'importo pagato (`moneyPaid`), il cliente, il negozio di contesto e i riferimenti Stripe.\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Stripe.","parameters":[{"schema":{"type":"string","enum":["PRIZE","COUPON","GIFT_CARD"]},"in":"query","name":"purchaseType","required":false},{"schema":{"type":"integer"},"in":"query","name":"customerId","required":false},{"schema":{"type":"integer"},"in":"query","name":"shopId","required":false},{"schema":{"type":"string","format":"date-time"},"in":"query","name":"dateFrom","required":false},{"schema":{"type":"string","format":"date-time"},"in":"query","name":"dateTo","required":false},{"schema":{"type":"string"},"in":"query","name":"paymentIntentId","required":false},{"schema":{"type":"string"},"in":"query","name":"status","required":false,"description":"Stato del pagamento Stripe: SUCCEEDED, CANCELED, REFUNDED"},{"schema":{"type":"integer","default":1},"in":"query","name":"page","required":false},{"schema":{"type":"integer","default":20},"in":"query","name":"size","required":false},{"schema":{"type":"integer"},"in":"path","name":"programId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Pagamenti Stripe del programma (Premio, Coupon, Gift Card). Ogni item descrive l'oggetto acquistato (`itemTitle`, `itemSubtitle`), l'importo pagato (`moneyPaid`), il cliente, il negozio di contesto e i riferimenti Stripe.","content":{"application/json":{"schema":{"description":"Pagamenti Stripe del programma (Premio, Coupon, Gift Card). Ogni item descrive l'oggetto acquistato (`itemTitle`, `itemSubtitle`), l'importo pagato (`moneyPaid`), il cliente, il negozio di contesto e i riferimenti Stripe.","type":"object","additionalProperties":true,"example":{"page":1,"size":20,"totalElements":3,"totalPages":1,"items":[{"purchaseType":"GIFT_CARD","sourceId":501,"customerId":42,"contextShopId":3,"shopName":"Negozio centro","itemTitle":"Gift Card 50€","itemSubtitle":"50 EUR","moneyPaid":45,"stripePaymentIntentId":"pi_3Pabc123","stripeCheckoutSessionId":"cs_test_a1b2c3","paidAt":"2026-05-15T10:00:00.000Z"},{"purchaseType":"COUPON","sourceId":88,"customerId":42,"contextShopId":3,"shopName":"Negozio centro","itemTitle":"Sconto 20% cena","itemSubtitle":"Valido sabato sera","moneyPaid":9.9,"stripePaymentIntentId":"pi_3Pxyz789","stripeCheckoutSessionId":"cs_test_d4e5f6","paidAt":"2026-05-15T10:00:00.000Z"},{"purchaseType":"PRIZE","sourceId":15,"customerId":42,"contextShopId":null,"shopName":null,"itemTitle":"Set colazione","itemSubtitle":"1","moneyPaid":12.5,"stripePaymentIntentId":"pi_3Ppri456","stripeCheckoutSessionId":null,"paidAt":"2026-05-15T10:00:00.000Z"}]}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/donation-ongs":{"get":{"tags":["DonationOngs"],"description":"Elenco paginato di progetto/ONG donazione del programma indicato in `programId`.\n\n**Contesto:** Le **ONG/Progetti donazione** sono campagne solidali con obiettivo di raccolta, immagini, indirizzo e statistiche aderenti.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n\n**Querystring:**\n- `page` (integer) — opzionale [default: 1]\n- `size` (integer) — opzionale [default: 20]\n- `search` (string) — opzionale: Filtro per nome (ILIKE)\n- `status` (string) — opzionale: Filtro stato progetto (ENABLED, DISABLED, TERMINATED) (valori: ENABLED, DISABLED, TERMINATED)\n- `provincia` (string) — opzionale: Filtro provincia dell'indirizzo del progetto (campo address.state, match parziale)\n\n**Risposte di successo:**\n- **200**: Lista donation-ongs\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** DonationOngs.","parameters":[{"schema":{"type":"integer","default":1},"in":"query","name":"page","required":false},{"schema":{"type":"integer","default":20},"in":"query","name":"size","required":false},{"schema":{"type":"string"},"in":"query","name":"search","required":false,"description":"Filtro per nome (ILIKE)"},{"schema":{"type":"string","enum":["ENABLED","DISABLED","TERMINATED"]},"in":"query","name":"status","required":false,"description":"Filtro stato progetto (ENABLED, DISABLED, TERMINATED)"},{"schema":{"type":"string"},"in":"query","name":"provincia","required":false,"description":"Filtro provincia dell'indirizzo del progetto (campo address.state, match parziale)"},{"schema":{"type":"integer"},"in":"path","name":"programId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Lista donation-ongs","content":{"application/json":{"schema":{"type":"object","properties":{"content":{"type":"array","items":{"type":"object","properties":{"id":{"type":"integer"},"programId":{"type":"integer"},"name":{"type":"string"},"description":{"type":"string","nullable":true},"status":{"type":"string","enum":["ENABLED","DISABLED","TERMINATED"]},"mainImageUrl":{"type":"string","nullable":true},"images":{"type":"array","items":{"type":"object","properties":{"id":{"type":"integer"},"url":{"type":"string"},"sortOrder":{"type":"integer"}},"required":["id","url","sortOrder"]}},"goalAmount":{"type":"number","nullable":true},"goalCurrencyId":{"type":"integer","nullable":true},"stopDonationsOnGoalReached":{"type":"boolean","description":"Si true, al raggiungimento di goalAmount non si accettano altre donazioni."},"adherentCount":{"type":"integer"},"donationsCount":{"type":"integer"},"donationsTotalAmount":{"type":"number"},"addressId":{"type":"integer","nullable":true},"address":{"type":"object","properties":{"id":{"type":"integer"},"street":{"type":"string","nullable":true},"number":{"type":"string","nullable":true},"floor":{"type":"string","nullable":true},"apartment":{"type":"string","nullable":true},"zipCode":{"type":"string","nullable":true},"city":{"type":"string","nullable":true},"state":{"type":"string","description":"Provincia","nullable":true},"country":{"type":"string","nullable":true},"countryId":{"type":"integer","nullable":true},"latitude":{"type":"number","nullable":true},"longitude":{"type":"number","nullable":true},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"nullable":true},"association":{"type":"object","properties":{"name":{"type":"string","nullable":true},"address":{"type":"string","nullable":true},"phone":{"type":"string","nullable":true},"email":{"type":"string","nullable":true},"website":{"type":"string","nullable":true},"openingHours":{"type":"array","items":{"type":"object","properties":{"day":{"type":"string","enum":["MONDAY","TUESDAY","WEDNESDAY","THURSDAY","FRIDAY","SATURDAY","SUNDAY"]},"ranges":{"type":"array","items":{"type":"object","properties":{"start":{"type":"string","example":"09:00"},"end":{"type":"string","example":"18:00"}},"required":["start","end"]}}},"required":["day","ranges"]}}}},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"required":["id","programId","name","description","status","mainImageUrl","images","goalAmount","goalCurrencyId","stopDonationsOnGoalReached","adherentCount","donationsCount","donationsTotalAmount","addressId","address","association","createdAt","updatedAt"]}},"totalElements":{"type":"integer"},"totalPages":{"type":"integer"},"page":{"type":"integer"},"size":{"type":"integer"}},"required":["content","totalElements","totalPages","page","size"],"description":"Lista donation-ongs","example":{"content":[{"id":1,"programId":1,"name":"Fondazione Esempio","description":"Descrizione campagna","status":"ENABLED","mainImageUrl":"https://cdn.example/ong-cover.jpg","images":[{"id":10,"url":"https://cdn.example/ong-1.jpg","sortOrder":0},{"id":11,"url":"https://cdn.example/ong-2.jpg","sortOrder":1}],"goalAmount":50000,"goalCurrencyId":1,"stopDonationsOnGoalReached":true,"adherentCount":12,"donationsCount":48,"donationsTotalAmount":1250.5,"addressId":10,"address":{"id":10,"street":"Via Roma","number":"1","floor":null,"apartment":null,"zipCode":"20100","city":"Milano","state":"MI","country":"IT","countryId":1,"latitude":45.4642,"longitude":9.19,"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"},"association":{"name":"Associazione Benefica APS","address":"Via Roma 1, 20100 Milano","phone":"+39 02 1234567","email":"info@esempio.org","website":"https://www.esempio.org","openingHours":[{"day":"MONDAY","ranges":[{"start":"09:00","end":"13:00"},{"start":"14:00","end":"18:00"}]}]},"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}],"totalElements":1,"totalPages":1,"page":1,"size":20},"additionalProperties":true}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"post":{"tags":["DonationOngs"],"description":"Crea progetto/ONG donazione del programma indicato in `programId`.\n\n**Contesto:** Le **ONG/Progetti donazione** sono campagne solidali con obiettivo di raccolta, immagini, indirizzo e statistiche aderenti.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n\n**Body JSON:**\n- `name` (string) — obbligatorio\n- `description` (string) — opzionale\n- `status` (string) — opzionale (valori: ENABLED, DISABLED)\n- `mainImageUrl` (string) — opzionale\n- `images` (array) — opzionale\n- `goalAmount` (number) — opzionale\n- `goalCurrencyId` (integer) — opzionale\n- `stopDonationsOnGoalReached` (boolean) — opzionale: Si true, al raggiungimento di goalAmount non si accettano altre donazioni.\n- `association` (object) — opzionale\n- `addressId` (integer) — opzionale: ID indirizzo esistente (alternativa a address)\n- `address` (object) — opzionale\n\n**Risposte di successo:**\n- **201**: Risorsa creata (donation-ongs)\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** DonationOngs.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"description":{"type":"string","nullable":true},"status":{"type":"string","enum":["ENABLED","DISABLED"]},"mainImageUrl":{"type":"string","nullable":true},"images":{"type":"array","items":{"type":"object","properties":{"url":{"type":"string"},"sortOrder":{"type":"integer"}},"required":["url"]}},"goalAmount":{"type":"number","nullable":true},"goalCurrencyId":{"type":"integer","nullable":true},"stopDonationsOnGoalReached":{"type":"boolean","description":"Si true, al raggiungimento di goalAmount non si accettano altre donazioni."},"association":{"type":"object","properties":{"name":{"type":"string","nullable":true},"address":{"type":"string","nullable":true},"phone":{"type":"string","nullable":true},"email":{"type":"string","nullable":true},"website":{"type":"string","nullable":true},"openingHours":{"type":"array","items":{"type":"object","properties":{"day":{"type":"string","enum":["MONDAY","TUESDAY","WEDNESDAY","THURSDAY","FRIDAY","SATURDAY","SUNDAY"]},"ranges":{"type":"array","items":{"type":"object","properties":{"start":{"type":"string","example":"09:00"},"end":{"type":"string","example":"18:00"}},"required":["start","end"]}}},"required":["day","ranges"]}}}},"addressId":{"type":"integer","description":"ID indirizzo esistente (alternativa a address)","nullable":true},"address":{"anyOf":[{"type":"null"},{"type":"object","properties":{"street":{"type":"string"},"number":{"type":"string"},"floor":{"type":"string"},"apartment":{"type":"string"},"zipCode":{"type":"string"},"city":{"type":"string"},"state":{"type":"string","description":"Provincia (sigla ISTAT per IT, es. FI, BO, RM)"},"country":{"type":"string","description":"Codice paese ISO (es. IT)"},"latitude":{"type":"number","nullable":true},"longitude":{"type":"number","nullable":true}}}],"description":"Indirizzo progetto; null = nessun indirizzo in create, o scollega in update"}},"required":["name"]}}},"required":true},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"201":{"description":"Risorsa creata (donation-ongs)","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"integer"},"programId":{"type":"integer"},"name":{"type":"string"},"description":{"type":"string","nullable":true},"status":{"type":"string","enum":["ENABLED","DISABLED","TERMINATED"]},"mainImageUrl":{"type":"string","nullable":true},"images":{"type":"array","items":{"type":"object","properties":{"id":{"type":"integer"},"url":{"type":"string"},"sortOrder":{"type":"integer"}},"required":["id","url","sortOrder"]}},"goalAmount":{"type":"number","nullable":true},"goalCurrencyId":{"type":"integer","nullable":true},"stopDonationsOnGoalReached":{"type":"boolean","description":"Si true, al raggiungimento di goalAmount non si accettano altre donazioni."},"adherentCount":{"type":"integer"},"donationsCount":{"type":"integer"},"donationsTotalAmount":{"type":"number"},"addressId":{"type":"integer","nullable":true},"address":{"type":"object","properties":{"id":{"type":"integer"},"street":{"type":"string","nullable":true},"number":{"type":"string","nullable":true},"floor":{"type":"string","nullable":true},"apartment":{"type":"string","nullable":true},"zipCode":{"type":"string","nullable":true},"city":{"type":"string","nullable":true},"state":{"type":"string","description":"Provincia","nullable":true},"country":{"type":"string","nullable":true},"countryId":{"type":"integer","nullable":true},"latitude":{"type":"number","nullable":true},"longitude":{"type":"number","nullable":true},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"nullable":true},"association":{"type":"object","properties":{"name":{"type":"string","nullable":true},"address":{"type":"string","nullable":true},"phone":{"type":"string","nullable":true},"email":{"type":"string","nullable":true},"website":{"type":"string","nullable":true},"openingHours":{"type":"array","items":{"type":"object","properties":{"day":{"type":"string","enum":["MONDAY","TUESDAY","WEDNESDAY","THURSDAY","FRIDAY","SATURDAY","SUNDAY"]},"ranges":{"type":"array","items":{"type":"object","properties":{"start":{"type":"string","example":"09:00"},"end":{"type":"string","example":"18:00"}},"required":["start","end"]}}},"required":["day","ranges"]}}}},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"required":["id","programId","name","description","status","mainImageUrl","images","goalAmount","goalCurrencyId","stopDonationsOnGoalReached","adherentCount","donationsCount","donationsTotalAmount","addressId","address","association","createdAt","updatedAt"],"description":"Risorsa creata (donation-ongs)","example":{"id":1,"programId":1,"name":"Fondazione Esempio","description":"Descrizione campagna","status":"ENABLED","mainImageUrl":"https://cdn.example/ong-cover.jpg","images":[{"id":10,"url":"https://cdn.example/ong-1.jpg","sortOrder":0},{"id":11,"url":"https://cdn.example/ong-2.jpg","sortOrder":1}],"goalAmount":50000,"goalCurrencyId":1,"stopDonationsOnGoalReached":true,"adherentCount":12,"donationsCount":48,"donationsTotalAmount":1250.5,"addressId":10,"address":{"id":10,"street":"Via Roma","number":"1","floor":null,"apartment":null,"zipCode":"20100","city":"Milano","state":"MI","country":"IT","countryId":1,"latitude":45.4642,"longitude":9.19,"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"},"association":{"name":"Associazione Benefica APS","address":"Via Roma 1, 20100 Milano","phone":"+39 02 1234567","email":"info@esempio.org","website":"https://www.esempio.org","openingHours":[{"day":"MONDAY","ranges":[{"start":"09:00","end":"13:00"},{"start":"14:00","end":"18:00"}]}]},"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"},"additionalProperties":true}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/donation-ongs/{id}":{"get":{"tags":["DonationOngs"],"description":"Dettaglio di una programma fedeltà per identificativo del programma indicato in `programId`.\n\n**Contesto:** Le **ONG/Progetti donazione** sono campagne solidali con obiettivo di raccolta, immagini, indirizzo e statistiche aderenti.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n- `id` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Dettaglio donation-ongs\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** DonationOngs.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Dettaglio donation-ongs","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"integer"},"programId":{"type":"integer"},"name":{"type":"string"},"description":{"type":"string","nullable":true},"status":{"type":"string","enum":["ENABLED","DISABLED","TERMINATED"]},"mainImageUrl":{"type":"string","nullable":true},"images":{"type":"array","items":{"type":"object","properties":{"id":{"type":"integer"},"url":{"type":"string"},"sortOrder":{"type":"integer"}},"required":["id","url","sortOrder"]}},"goalAmount":{"type":"number","nullable":true},"goalCurrencyId":{"type":"integer","nullable":true},"stopDonationsOnGoalReached":{"type":"boolean","description":"Si true, al raggiungimento di goalAmount non si accettano altre donazioni."},"adherentCount":{"type":"integer"},"donationsCount":{"type":"integer"},"donationsTotalAmount":{"type":"number"},"addressId":{"type":"integer","nullable":true},"address":{"type":"object","properties":{"id":{"type":"integer"},"street":{"type":"string","nullable":true},"number":{"type":"string","nullable":true},"floor":{"type":"string","nullable":true},"apartment":{"type":"string","nullable":true},"zipCode":{"type":"string","nullable":true},"city":{"type":"string","nullable":true},"state":{"type":"string","description":"Provincia","nullable":true},"country":{"type":"string","nullable":true},"countryId":{"type":"integer","nullable":true},"latitude":{"type":"number","nullable":true},"longitude":{"type":"number","nullable":true},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"nullable":true},"association":{"type":"object","properties":{"name":{"type":"string","nullable":true},"address":{"type":"string","nullable":true},"phone":{"type":"string","nullable":true},"email":{"type":"string","nullable":true},"website":{"type":"string","nullable":true},"openingHours":{"type":"array","items":{"type":"object","properties":{"day":{"type":"string","enum":["MONDAY","TUESDAY","WEDNESDAY","THURSDAY","FRIDAY","SATURDAY","SUNDAY"]},"ranges":{"type":"array","items":{"type":"object","properties":{"start":{"type":"string","example":"09:00"},"end":{"type":"string","example":"18:00"}},"required":["start","end"]}}},"required":["day","ranges"]}}}},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"required":["id","programId","name","description","status","mainImageUrl","images","goalAmount","goalCurrencyId","stopDonationsOnGoalReached","adherentCount","donationsCount","donationsTotalAmount","addressId","address","association","createdAt","updatedAt"],"description":"Dettaglio donation-ongs","example":{"id":1,"programId":1,"name":"Fondazione Esempio","description":"Descrizione campagna","status":"ENABLED","mainImageUrl":"https://cdn.example/ong-cover.jpg","images":[{"id":10,"url":"https://cdn.example/ong-1.jpg","sortOrder":0},{"id":11,"url":"https://cdn.example/ong-2.jpg","sortOrder":1}],"goalAmount":50000,"goalCurrencyId":1,"stopDonationsOnGoalReached":true,"adherentCount":12,"donationsCount":48,"donationsTotalAmount":1250.5,"addressId":10,"address":{"id":10,"street":"Via Roma","number":"1","floor":null,"apartment":null,"zipCode":"20100","city":"Milano","state":"MI","country":"IT","countryId":1,"latitude":45.4642,"longitude":9.19,"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"},"association":{"name":"Associazione Benefica APS","address":"Via Roma 1, 20100 Milano","phone":"+39 02 1234567","email":"info@esempio.org","website":"https://www.esempio.org","openingHours":[{"day":"MONDAY","ranges":[{"start":"09:00","end":"13:00"},{"start":"14:00","end":"18:00"}]}]},"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"},"additionalProperties":true}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"put":{"tags":["DonationOngs"],"description":"Aggiorna una programma fedeltà (campi consentiti) del programma indicato in `programId`.\n\n**Contesto:** Le **ONG/Progetti donazione** sono campagne solidali con obiettivo di raccolta, immagini, indirizzo e statistiche aderenti.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n- `id` (integer) — obbligatorio\n\n**Body JSON:**\n- `name` (string) — opzionale\n- `description` (string) — opzionale\n- `status` (string) — opzionale (valori: ENABLED, DISABLED)\n- `mainImageUrl` (string) — opzionale\n- `images` (array) — opzionale\n- `goalAmount` (number) — opzionale\n- `goalCurrencyId` (integer) — opzionale\n- `stopDonationsOnGoalReached` (boolean) — opzionale: Si true, al raggiungimento di goalAmount non si accettano altre donazioni.\n- `association` (object) — opzionale\n- `addressId` (integer) — opzionale: ID indirizzo esistente (alternativa a address)\n- `address` (object) — opzionale\n\n**Risposte di successo:**\n- **200**: Aggiornamento donation-ongs\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** DonationOngs.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"description":{"type":"string","nullable":true},"status":{"type":"string","enum":["ENABLED","DISABLED"]},"mainImageUrl":{"type":"string","nullable":true},"images":{"type":"array","items":{"type":"object","properties":{"url":{"type":"string"},"sortOrder":{"type":"integer"}},"required":["url"]}},"goalAmount":{"type":"number","nullable":true},"goalCurrencyId":{"type":"integer","nullable":true},"stopDonationsOnGoalReached":{"type":"boolean","description":"Si true, al raggiungimento di goalAmount non si accettano altre donazioni."},"association":{"type":"object","properties":{"name":{"type":"string","nullable":true},"address":{"type":"string","nullable":true},"phone":{"type":"string","nullable":true},"email":{"type":"string","nullable":true},"website":{"type":"string","nullable":true},"openingHours":{"type":"array","items":{"type":"object","properties":{"day":{"type":"string","enum":["MONDAY","TUESDAY","WEDNESDAY","THURSDAY","FRIDAY","SATURDAY","SUNDAY"]},"ranges":{"type":"array","items":{"type":"object","properties":{"start":{"type":"string","example":"09:00"},"end":{"type":"string","example":"18:00"}},"required":["start","end"]}}},"required":["day","ranges"]}}}},"addressId":{"type":"integer","description":"ID indirizzo esistente (alternativa a address)","nullable":true},"address":{"anyOf":[{"type":"null"},{"type":"object","properties":{"street":{"type":"string"},"number":{"type":"string"},"floor":{"type":"string"},"apartment":{"type":"string"},"zipCode":{"type":"string"},"city":{"type":"string"},"state":{"type":"string","description":"Provincia (sigla ISTAT per IT, es. FI, BO, RM)"},"country":{"type":"string","description":"Codice paese ISO (es. IT)"},"latitude":{"type":"number","nullable":true},"longitude":{"type":"number","nullable":true}}}],"description":"Indirizzo progetto; null = nessun indirizzo in create, o scollega in update"}}}}}},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Aggiornamento donation-ongs","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"integer"},"programId":{"type":"integer"},"name":{"type":"string"},"description":{"type":"string","nullable":true},"status":{"type":"string","enum":["ENABLED","DISABLED","TERMINATED"]},"mainImageUrl":{"type":"string","nullable":true},"images":{"type":"array","items":{"type":"object","properties":{"id":{"type":"integer"},"url":{"type":"string"},"sortOrder":{"type":"integer"}},"required":["id","url","sortOrder"]}},"goalAmount":{"type":"number","nullable":true},"goalCurrencyId":{"type":"integer","nullable":true},"stopDonationsOnGoalReached":{"type":"boolean","description":"Si true, al raggiungimento di goalAmount non si accettano altre donazioni."},"adherentCount":{"type":"integer"},"donationsCount":{"type":"integer"},"donationsTotalAmount":{"type":"number"},"addressId":{"type":"integer","nullable":true},"address":{"type":"object","properties":{"id":{"type":"integer"},"street":{"type":"string","nullable":true},"number":{"type":"string","nullable":true},"floor":{"type":"string","nullable":true},"apartment":{"type":"string","nullable":true},"zipCode":{"type":"string","nullable":true},"city":{"type":"string","nullable":true},"state":{"type":"string","description":"Provincia","nullable":true},"country":{"type":"string","nullable":true},"countryId":{"type":"integer","nullable":true},"latitude":{"type":"number","nullable":true},"longitude":{"type":"number","nullable":true},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"nullable":true},"association":{"type":"object","properties":{"name":{"type":"string","nullable":true},"address":{"type":"string","nullable":true},"phone":{"type":"string","nullable":true},"email":{"type":"string","nullable":true},"website":{"type":"string","nullable":true},"openingHours":{"type":"array","items":{"type":"object","properties":{"day":{"type":"string","enum":["MONDAY","TUESDAY","WEDNESDAY","THURSDAY","FRIDAY","SATURDAY","SUNDAY"]},"ranges":{"type":"array","items":{"type":"object","properties":{"start":{"type":"string","example":"09:00"},"end":{"type":"string","example":"18:00"}},"required":["start","end"]}}},"required":["day","ranges"]}}}},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"required":["id","programId","name","description","status","mainImageUrl","images","goalAmount","goalCurrencyId","stopDonationsOnGoalReached","adherentCount","donationsCount","donationsTotalAmount","addressId","address","association","createdAt","updatedAt"],"description":"Aggiornamento donation-ongs","example":{"id":1,"programId":1,"name":"Fondazione Esempio","description":"Descrizione campagna","status":"ENABLED","mainImageUrl":"https://cdn.example/ong-cover.jpg","images":[{"id":10,"url":"https://cdn.example/ong-1.jpg","sortOrder":0},{"id":11,"url":"https://cdn.example/ong-2.jpg","sortOrder":1}],"goalAmount":50000,"goalCurrencyId":1,"stopDonationsOnGoalReached":true,"adherentCount":12,"donationsCount":48,"donationsTotalAmount":1250.5,"addressId":10,"address":{"id":10,"street":"Via Roma","number":"1","floor":null,"apartment":null,"zipCode":"20100","city":"Milano","state":"MI","country":"IT","countryId":1,"latitude":45.4642,"longitude":9.19,"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"},"association":{"name":"Associazione Benefica APS","address":"Via Roma 1, 20100 Milano","phone":"+39 02 1234567","email":"info@esempio.org","website":"https://www.esempio.org","openingHours":[{"day":"MONDAY","ranges":[{"start":"09:00","end":"13:00"},{"start":"14:00","end":"18:00"}]}]},"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"},"additionalProperties":true}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"delete":{"tags":["DonationOngs"],"description":"Elimina una programma fedeltà del programma indicato in `programId` (soft delete o `TERMINATED` se vincoli FK).\n\n**Contesto:** Le **ONG/Progetti donazione** sono campagne solidali con obiettivo di raccolta, immagini, indirizzo e statistiche aderenti.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n- `id` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **204**: Operazione riuscita senza corpo di risposta.\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** DonationOngs.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"204":{"description":"Operazione riuscita senza corpo di risposta."},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/donation-settings":{"get":{"tags":["DonationOngs"],"description":"Elenco paginato di donation-settings del programma indicato in `programId`.\n\n**Contesto:** Le **ONG/Progetti donazione** sono campagne solidali con obiettivo di raccolta, immagini, indirizzo e statistiche aderenti.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Lista donation-settings\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** DonationOngs.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Lista donation-settings","content":{"application/json":{"schema":{"type":"object","properties":{"enabled":{"type":"boolean"},"maxProjectsPerCustomer":{"type":"integer","minimum":1,"maximum":50},"distributionMode":{"type":"string","enum":["HOMOGENEOUS","FREE"]}},"required":["enabled","maxProjectsPerCustomer","distributionMode"],"description":"Lista donation-settings","example":{"content":[{"id":1,"name":"Esempio","programId":1,"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}],"totalElements":1,"totalPages":1,"page":1,"size":20},"additionalProperties":true}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"put":{"tags":["DonationOngs"],"description":"Aggiorna una programma fedeltà (campi consentiti) del programma indicato in `programId`.\n\n**Contesto:** Le **ONG/Progetti donazione** sono campagne solidali con obiettivo di raccolta, immagini, indirizzo e statistiche aderenti.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n\n**Body JSON:**\n- `enabled` (boolean) — obbligatorio\n- `maxProjectsPerCustomer` (integer) — obbligatorio\n- `distributionMode` (string) — obbligatorio (valori: HOMOGENEOUS, FREE)\n\n**Risposte di successo:**\n- **200**: Default Response\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** DonationOngs.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"enabled":{"type":"boolean"},"maxProjectsPerCustomer":{"type":"integer","minimum":1,"maximum":50},"distributionMode":{"type":"string","enum":["HOMOGENEOUS","FREE"]}},"required":["enabled","maxProjectsPerCustomer","distributionMode"]}}},"required":true},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"enabled":{"type":"boolean"},"maxProjectsPerCustomer":{"type":"integer","minimum":1,"maximum":50},"distributionMode":{"type":"string","enum":["HOMOGENEOUS","FREE"]}},"required":["enabled","maxProjectsPerCustomer","distributionMode"],"example":{"enabled":false,"maxProjectsPerCustomer":0,"distributionMode":"HOMOGENEOUS"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/customers/{id}/donation-profile":{"get":{"tags":["Donations"],"description":"Elenco paginato di donation-profile.\n\n**Contesto:** Le **Donazioni** trasferiscono unità wallet del cliente verso progetti/ONG solidali configurati nel programma.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `id` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Profilo donazioni cliente (adesioni e ripartizione).\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Donations.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Profilo donazioni cliente (adesioni e ripartizione).","content":{"application/json":{"schema":{"description":"Profilo donazioni cliente (adesioni e ripartizione).","type":"object","additionalProperties":true,"example":{"programId":1,"donationsEnabled":true,"distributionMode":"HOMOGENEOUS","maxProjectsPerCustomer":3,"selfShareEnabled":true,"selfSharePercent":10,"adhesions":[{"donationOngId":1,"donationOngName":"Progetto solidale demo","donationPercent":45,"donationsCount":2,"donationsTotalAmount":12.5,"updatedAt":"2026-05-15T10:00:00.000Z"}]}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"put":{"tags":["Donations"],"description":"Aggiorna donation-profile (campi consentiti).\n\n**Contesto:** Le **Donazioni** trasferiscono unità wallet del cliente verso progetti/ONG solidali configurati nel programma.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `id` (integer) — obbligatorio\n\n**Body JSON:**\n- `selfShareEnabled` (boolean) — opzionale\n- `adhesions` (array) — opzionale\n\n**Risposte di successo:**\n- **200**: Profilo donazioni cliente (adesioni e ripartizione).\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Donations.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"selfShareEnabled":{"type":"boolean"},"adhesions":{"type":"array","items":{"type":"object","required":["donationOngId"],"properties":{"donationOngId":{"type":"integer"},"donationPercent":{"type":"number"}}}}}}}}},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Profilo donazioni cliente (adesioni e ripartizione).","content":{"application/json":{"schema":{"description":"Profilo donazioni cliente (adesioni e ripartizione).","type":"object","additionalProperties":true,"example":{"programId":1,"donationsEnabled":true,"distributionMode":"HOMOGENEOUS","maxProjectsPerCustomer":3,"selfShareEnabled":true,"selfSharePercent":10,"adhesions":[{"donationOngId":1,"donationOngName":"Progetto solidale demo","donationPercent":45,"donationsCount":2,"donationsTotalAmount":12.5,"updatedAt":"2026-05-15T10:00:00.000Z"}]}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/customers/{id}/donations":{"get":{"tags":["Donations"],"description":"Elenco paginato di donazione.\n\n**Contesto:** Le **Donazioni** trasferiscono unità wallet del cliente verso progetti/ONG solidali configurati nel programma.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `id` (integer) — obbligatorio\n\n**Querystring:**\n- `donationOngId` (integer) — opzionale\n- `page` (integer) — opzionale [default: 1]\n- `size` (integer) — opzionale [default: 20]\n\n**Risposte di successo:**\n- **200**: Lista donazioni paginata.\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Donations.","parameters":[{"schema":{"type":"integer"},"in":"query","name":"donationOngId","required":false},{"schema":{"type":"integer","default":1},"in":"query","name":"page","required":false},{"schema":{"type":"integer","default":20},"in":"query","name":"size","required":false},{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Lista donazioni paginata.","content":{"application/json":{"schema":{"description":"Lista donazioni paginata.","type":"object","additionalProperties":true,"example":{"content":[{"id":1,"programId":1,"customerId":2,"donationOngId":1,"donationOngName":"Progetto solidale demo","walletId":1,"amount":5,"currencyId":1,"donationType":"ONE_TIME","sourceMovementId":null,"donationMovementId":10064,"createdAt":"2026-05-15T10:00:00.000Z"}],"totalElements":1,"totalPages":1,"page":1,"size":20}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/shop-categories":{"get":{"tags":["ShopCategories"],"description":"List shop categories (system + program-specific). CUSTOMER/POS/BACKEND: solo categorie abilitate. PLATFORM: tutte (anche disabilitate, status DISABLED); opzionale enabledOnly=true per filtrare.\n\n**Contesto:** Le **Categorie negozio** classificano i punti vendita (con traduzioni multilingua e sottocategorie). Usate in filtri lista negozi e in APP.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n\n**Querystring:**\n- `lang` (string) — opzionale: Language for name (valori: en, it, es, fr, de, pt)\n- `parentId` (integer) — opzionale: Filter by parent (omit for all; use rootsOnly for top-level only)\n- `rootsOnly` (boolean) — opzionale: Only top-level categories (parent_id IS NULL)\n- `tree` (boolean) — opzionale: Return nested tree with subcategories array\n- `enabledOnly` (boolean) — opzionale: Only categories effectively enabled (self + parent)\n- `page` (integer) — opzionale [default: 1]\n- `size` (integer) — opzionale [default: 20]\n\n**Risposte di successo:**\n- **200**: Lista shop-categories\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** ShopCategories.","parameters":[{"schema":{"type":"string","enum":["en","it","es","fr","de","pt"]},"in":"query","name":"lang","required":false,"description":"Language for name"},{"schema":{"type":"integer"},"in":"query","name":"parentId","required":false,"description":"Filter by parent (omit for all; use rootsOnly for top-level only)"},{"schema":{"type":"boolean"},"in":"query","name":"rootsOnly","required":false,"description":"Only top-level categories (parent_id IS NULL)"},{"schema":{"type":"boolean"},"in":"query","name":"tree","required":false,"description":"Return nested tree with subcategories array"},{"schema":{"type":"boolean"},"in":"query","name":"enabledOnly","required":false,"description":"Only categories effectively enabled (self + parent)"},{"schema":{"type":"integer","default":1},"in":"query","name":"page","required":false},{"schema":{"type":"integer","default":20},"in":"query","name":"size","required":false},{"schema":{"type":"integer"},"in":"path","name":"programId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Lista shop-categories","content":{"application/json":{"schema":{"description":"Lista shop-categories","type":"object","additionalProperties":true,"example":{"content":[{"id":1,"programId":1,"nameIt":"Bar","nameEn":"Bar","nameEs":null,"nameFr":null,"nameDe":null,"namePt":null,"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}],"totalElements":1,"totalPages":1,"page":1,"size":20}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"post":{"tags":["ShopCategories"],"description":"Crea categoria negozio del programma indicato in `programId`.\n\n**Contesto:** Le **Categorie negozio** classificano i punti vendita (con traduzioni multilingua e sottocategorie). Usate in filtri lista negozi e in APP.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n\n**Body JSON:**\n- `parentId` (integer) — opzionale\n- `enabled` (boolean) — opzionale\n- `nameEn` (string) — opzionale\n- `nameIt` (string) — opzionale\n- `nameEs` (string) — opzionale\n- `nameFr` (string) — opzionale\n- `nameDe` (string) — opzionale\n- `namePt` (string) — opzionale\n\n**Risposte di successo:**\n- **201**: Risorsa creata (shop-categories)\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** ShopCategories.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"parentId":{"type":"integer","nullable":true},"enabled":{"type":"boolean"},"nameEn":{"type":"string"},"nameIt":{"type":"string"},"nameEs":{"type":"string"},"nameFr":{"type":"string"},"nameDe":{"type":"string"},"namePt":{"type":"string"}}}}}},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"201":{"description":"Risorsa creata (shop-categories)","content":{"application/json":{"schema":{"description":"Risorsa creata (shop-categories)","type":"object","additionalProperties":true,"example":{"id":1,"programId":1,"nameIt":"Bar","nameEn":"Bar","nameEs":null,"nameFr":null,"nameDe":null,"namePt":null,"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/shop-categories/{id}":{"get":{"tags":["ShopCategories"],"description":"Dettaglio di una programma fedeltà per identificativo del programma indicato in `programId`.\n\n**Contesto:** Le **Categorie negozio** classificano i punti vendita (con traduzioni multilingua e sottocategorie). Usate in filtri lista negozi e in APP.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n- `id` (integer) — obbligatorio\n\n**Querystring:**\n- `lang` (string) — opzionale (valori: en, it, es, fr, de, pt)\n\n**Risposte di successo:**\n- **200**: Dettaglio shop-categories\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** ShopCategories.","parameters":[{"schema":{"type":"string","enum":["en","it","es","fr","de","pt"]},"in":"query","name":"lang","required":false},{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Dettaglio shop-categories","content":{"application/json":{"schema":{"description":"Dettaglio shop-categories","type":"object","additionalProperties":true,"example":{"id":1,"programId":1,"nameIt":"Bar","nameEn":"Bar","nameEs":null,"nameFr":null,"nameDe":null,"namePt":null,"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"put":{"tags":["ShopCategories"],"description":"Update shop category. Disabling a parent category also disables its subcategories.\n\n**Contesto:** Le **Categorie negozio** classificano i punti vendita (con traduzioni multilingua e sottocategorie). Usate in filtri lista negozi e in APP.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n- `id` (integer) — obbligatorio\n\n**Body JSON:**\n- `parentId` (integer) — opzionale\n- `enabled` (boolean) — opzionale\n- `nameEn` (string) — opzionale\n- `nameIt` (string) — opzionale\n- `nameEs` (string) — opzionale\n- `nameFr` (string) — opzionale\n- `nameDe` (string) — opzionale\n- `namePt` (string) — opzionale\n\n**Risposte di successo:**\n- **200**: Aggiornamento shop-categories\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** ShopCategories.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"parentId":{"type":"integer","nullable":true},"enabled":{"type":"boolean"},"nameEn":{"type":"string"},"nameIt":{"type":"string"},"nameEs":{"type":"string"},"nameFr":{"type":"string"},"nameDe":{"type":"string"},"namePt":{"type":"string"}}}}}},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Aggiornamento shop-categories","content":{"application/json":{"schema":{"description":"Aggiornamento shop-categories","type":"object","additionalProperties":true,"example":{"id":1,"programId":1,"nameIt":"Bar","nameEn":"Bar","nameEs":null,"nameFr":null,"nameDe":null,"namePt":null,"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"delete":{"tags":["ShopCategories"],"description":"Delete shop category (soft delete). Only program-owned categories. Cascades to subcategories.\n\n**Contesto:** Le **Categorie negozio** classificano i punti vendita (con traduzioni multilingua e sottocategorie). Usate in filtri lista negozi e in APP.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n- `id` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **204**: Operazione riuscita senza corpo di risposta.\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** ShopCategories.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"204":{"description":"Operazione riuscita senza corpo di risposta."},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/product-categories":{"get":{"tags":["ProductCategories"],"description":"Elenco paginato di categoria prodotto del programma indicato in `programId`.\n\n**Contesto:** Le **Categorie prodotto** organizzano il catalogo prodotti usato nelle transazioni e nelle regole promo/acquisto.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n\n**Querystring:**\n- `page` (integer) — opzionale [default: 1]\n- `size` (integer) — opzionale [default: 20]\n\n**Risposte di successo:**\n- **200**: Lista product-categories\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** ProductCategories.","parameters":[{"schema":{"type":"integer","minimum":1,"default":1},"in":"query","name":"page","required":false},{"schema":{"type":"integer","minimum":1,"maximum":500,"default":20},"in":"query","name":"size","required":false},{"schema":{"type":"integer"},"in":"path","name":"programId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Lista product-categories","content":{"application/json":{"schema":{"description":"Lista product-categories","type":"object","additionalProperties":true,"example":{"content":[{"id":1,"programId":1,"name":"Bevande","description":null,"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}],"totalElements":1,"totalPages":1,"page":1,"size":20}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"post":{"tags":["ProductCategories"],"description":"Crea categoria prodotto del programma indicato in `programId`.\n\n**Contesto:** Le **Categorie prodotto** organizzano il catalogo prodotti usato nelle transazioni e nelle regole promo/acquisto.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n\n**Body JSON:**\n- `name` (string) — obbligatorio\n- `description` (string) — opzionale\n\n**Risposte di successo:**\n- **201**: Risorsa creata (product-categories)\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** ProductCategories.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["name"],"properties":{"name":{"type":"string"},"description":{"type":"string"}}}}},"required":true},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"201":{"description":"Risorsa creata (product-categories)","content":{"application/json":{"schema":{"description":"Risorsa creata (product-categories)","type":"object","additionalProperties":true,"example":{"id":1,"programId":1,"name":"Bevande","description":null,"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/product-categories/{id}":{"get":{"tags":["ProductCategories"],"description":"Dettaglio di una programma fedeltà per identificativo del programma indicato in `programId`.\n\n**Contesto:** Le **Categorie prodotto** organizzano il catalogo prodotti usato nelle transazioni e nelle regole promo/acquisto.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n- `id` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Dettaglio product-categories\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** ProductCategories.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Dettaglio product-categories","content":{"application/json":{"schema":{"description":"Dettaglio product-categories","type":"object","additionalProperties":true,"example":{"id":1,"programId":1,"name":"Bevande","description":null,"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"put":{"tags":["ProductCategories"],"description":"Aggiorna una programma fedeltà (campi consentiti) del programma indicato in `programId`.\n\n**Contesto:** Le **Categorie prodotto** organizzano il catalogo prodotti usato nelle transazioni e nelle regole promo/acquisto.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n- `id` (integer) — obbligatorio\n\n**Body JSON:**\n- `name` (string) — opzionale\n- `description` (string) — opzionale\n\n**Risposte di successo:**\n- **200**: Aggiornamento product-categories\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** ProductCategories.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"description":{"type":"string"}}}}}},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Aggiornamento product-categories","content":{"application/json":{"schema":{"description":"Aggiornamento product-categories","type":"object","additionalProperties":true,"example":{"id":1,"programId":1,"name":"Bevande","description":null,"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"delete":{"tags":["ProductCategories"],"description":"Elimina una programma fedeltà del programma indicato in `programId` (soft delete o `TERMINATED` se vincoli FK).\n\n**Contesto:** Le **Categorie prodotto** organizzano il catalogo prodotti usato nelle transazioni e nelle regole promo/acquisto.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n- `id` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **204**: Operazione riuscita senza corpo di risposta.\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** ProductCategories.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"204":{"description":"Operazione riuscita senza corpo di risposta."},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/products":{"get":{"tags":["Products"],"description":"Elenco paginato di prodotto del programma indicato in `programId`.\n\n**Contesto:** I **Prodotti** sono voci del catalogo acquistabile, collegabili a categorie e usate nei movimenti di vendita e nelle regole di calcolo.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n\n**Querystring:**\n- `categoryId` (integer) — opzionale\n- `netId` (integer) — opzionale\n- `search` (string) — opzionale\n- `page` (integer) — opzionale [default: 1]\n- `size` (integer) — opzionale [default: 20]\n\n**Risposte di successo:**\n- **200**: Lista products\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Products.","parameters":[{"schema":{"type":"integer"},"in":"query","name":"categoryId","required":false},{"schema":{"type":"integer"},"in":"query","name":"netId","required":false},{"schema":{"type":"string"},"in":"query","name":"search","required":false},{"schema":{"type":"integer","default":1},"in":"query","name":"page","required":false},{"schema":{"type":"integer","default":20},"in":"query","name":"size","required":false},{"schema":{"type":"integer"},"in":"path","name":"programId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Lista products","content":{"application/json":{"schema":{"description":"Lista products","type":"object","additionalProperties":true,"example":{"content":[{"id":1,"name":"Prodotto","description":null,"programId":1,"categoryId":1,"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}],"totalElements":1,"totalPages":1,"page":1,"size":20}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/products/{id}":{"get":{"tags":["Products"],"description":"Dettaglio di prodotto per identificativo.\n\n**Contesto:** I **Prodotti** sono voci del catalogo acquistabile, collegabili a categorie e usate nei movimenti di vendita e nelle regole di calcolo.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `id` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Prodotto\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Products.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Prodotto","content":{"application/json":{"schema":{"description":"Prodotto","type":"object","additionalProperties":true,"example":{"id":1,"name":"Prodotto","description":null,"programId":1,"categoryId":1,"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"put":{"tags":["Products"],"description":"Aggiorna prodotto (campi consentiti).\n\n**Contesto:** I **Prodotti** sono voci del catalogo acquistabile, collegabili a categorie e usate nei movimenti di vendita e nelle regole di calcolo.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `id` (integer) — obbligatorio\n\n**Body JSON:**\n- `name` (string) — opzionale\n- `description` (string) — opzionale\n- `categoryId` (integer) — opzionale\n\n**Risposte di successo:**\n- **200**: Prodotto aggiornato\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Products.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"description":{"type":"string"},"code":{"type":"string","nullable":true},"categoryId":{"type":"integer","nullable":true}}}}}},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Prodotto aggiornato","content":{"application/json":{"schema":{"description":"Prodotto aggiornato","type":"object","additionalProperties":true,"example":{"id":1,"name":"Prodotto","description":null,"programId":1,"categoryId":1,"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"delete":{"tags":["Products"],"description":"Elimina prodotto (soft delete o `TERMINATED` se vincoli FK).\n\n**Contesto:** I **Prodotti** sono voci del catalogo acquistabile, collegabili a categorie e usate nei movimenti di vendita e nelle regole di calcolo.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `id` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **204**: Operazione riuscita senza corpo di risposta.\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Products.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"204":{"description":"Operazione riuscita senza corpo di risposta."},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/products":{"post":{"tags":["Products"],"description":"Crea prodotto.\n\n**Contesto:** I **Prodotti** sono voci del catalogo acquistabile, collegabili a categorie e usate nei movimenti di vendita e nelle regole di calcolo.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Body JSON:**\n- `programId` (integer) — obbligatorio\n- `name` (string) — obbligatorio\n- `description` (string) — opzionale\n- `categoryId` (integer) — opzionale\n\n**Risposte di successo:**\n- **201**: Prodotto creato\n\n**Errori documentati:** 400, 401, 403, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Products.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["programId","name"],"properties":{"programId":{"type":"integer"},"name":{"type":"string"},"description":{"type":"string"},"code":{"type":"string","nullable":true},"categoryId":{"type":"integer","nullable":true}}}}},"required":true},"security":[{"bearerAuth":[]}],"responses":{"201":{"description":"Prodotto creato","content":{"application/json":{"schema":{"description":"Prodotto creato","type":"object","additionalProperties":true,"example":{"id":1,"name":"Prodotto","description":null,"programId":1,"categoryId":1,"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/fnet3web/interfaces":{"post":{"responses":{"200":{"description":"Default Response"}}}},"/api/v1/programs/{programId}/purchase-rules":{"get":{"tags":["PurchaseRules"],"description":"Elenco paginato di regola acquisto del programma indicato in `programId`.\n\n**Contesto:** Le **Regole acquisto** definiscono come mappare righe di scontrino/transazione su prodotti, categorie e wallet (campi variabili del movimento).\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n\n**Querystring:**\n- `page` (integer) — opzionale [default: 1]\n- `size` (integer) — opzionale [default: 20]\n\n**Risposte di successo:**\n- **200**: Lista purchase-rules\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** PurchaseRules.","parameters":[{"schema":{"type":"integer","minimum":1,"default":1},"in":"query","name":"page","required":false},{"schema":{"type":"integer","minimum":1,"maximum":500,"default":20},"in":"query","name":"size","required":false},{"schema":{"type":"integer"},"in":"path","name":"programId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Lista purchase-rules","content":{"application/json":{"schema":{"description":"Lista purchase-rules","type":"object","additionalProperties":true,"example":{"content":[{"id":1,"programId":1,"name":"Regola acquisto","priority":1,"enabled":true,"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}],"totalElements":1,"totalPages":1,"page":1,"size":20}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"post":{"tags":["PurchaseRules"],"description":"Crea regola acquisto del programma indicato in `programId`.\n\n**Contesto:** Le **Regole acquisto** definiscono come mappare righe di scontrino/transazione su prodotti, categorie e wallet (campi variabili del movimento).\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n\n**Body JSON:**\n- `code` (string) — obbligatorio\n- `name` (string) — obbligatorio\n- `type` (string) — obbligatorio (valori: STRING, NUMBER, BOOLEAN)\n- `required` (boolean) — opzionale [default: false]\n\n**Risposte di successo:**\n- **201**: Risorsa creata (purchase-rules)\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** PurchaseRules.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["code","name","type"],"properties":{"code":{"type":"string"},"name":{"type":"string"},"type":{"type":"string","enum":["STRING","NUMBER","BOOLEAN"]},"required":{"type":"boolean","default":false}}}}},"required":true},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"201":{"description":"Risorsa creata (purchase-rules)","content":{"application/json":{"schema":{"description":"Risorsa creata (purchase-rules)","type":"object","additionalProperties":true,"example":{"id":1,"programId":1,"name":"Regola acquisto","priority":1,"enabled":true,"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/purchase-rules/{id}":{"get":{"tags":["PurchaseRules"],"description":"Dettaglio di una programma fedeltà per identificativo del programma indicato in `programId`.\n\n**Contesto:** Le **Regole acquisto** definiscono come mappare righe di scontrino/transazione su prodotti, categorie e wallet (campi variabili del movimento).\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n- `id` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Dettaglio purchase-rules\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** PurchaseRules.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Dettaglio purchase-rules","content":{"application/json":{"schema":{"description":"Dettaglio purchase-rules","type":"object","additionalProperties":true,"example":{"id":1,"programId":1,"name":"Regola acquisto","priority":1,"enabled":true,"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"put":{"tags":["PurchaseRules"],"description":"Aggiorna una programma fedeltà (campi consentiti) del programma indicato in `programId`.\n\n**Contesto:** Le **Regole acquisto** definiscono come mappare righe di scontrino/transazione su prodotti, categorie e wallet (campi variabili del movimento).\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n- `id` (integer) — obbligatorio\n\n**Body JSON:**\n- `name` (string) — opzionale\n- `required` (boolean) — opzionale\n\n**Risposte di successo:**\n- **200**: Aggiornamento purchase-rules\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** PurchaseRules.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"required":{"type":"boolean"}}}}}},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Aggiornamento purchase-rules","content":{"application/json":{"schema":{"description":"Aggiornamento purchase-rules","type":"object","additionalProperties":true,"example":{"id":1,"programId":1,"name":"Regola acquisto","priority":1,"enabled":true,"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"delete":{"tags":["PurchaseRules"],"description":"Elimina una programma fedeltà del programma indicato in `programId` (soft delete o `TERMINATED` se vincoli FK).\n\n**Contesto:** Le **Regole acquisto** definiscono come mappare righe di scontrino/transazione su prodotti, categorie e wallet (campi variabili del movimento).\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n- `id` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **204**: Operazione riuscita senza corpo di risposta.\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** PurchaseRules.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"204":{"description":"Operazione riuscita senza corpo di risposta."},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/badges-categories":{"get":{"tags":["BadgesCategories"],"description":"Elenco paginato di categoria badge del programma indicato in `programId`.\n\n**Contesto:** Le **Categorie badge** raggruppano achievement/gamification assegnabili ai clienti (fedeltà, visita, milestone).\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n\n**Querystring:**\n- `page` (integer) — opzionale [default: 1]\n- `size` (integer) — opzionale [default: 20]\n\n**Risposte di successo:**\n- **200**: Lista badges-categories\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** BadgesCategories.","parameters":[{"schema":{"type":"integer","minimum":1,"default":1},"in":"query","name":"page","required":false},{"schema":{"type":"integer","minimum":1,"maximum":500,"default":20},"in":"query","name":"size","required":false},{"schema":{"type":"integer"},"in":"path","name":"programId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Lista badges-categories","content":{"application/json":{"schema":{"description":"Lista badges-categories","type":"object","additionalProperties":true,"example":{"content":[{"id":1,"programId":1,"name":"Categoria badge","description":null,"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}],"totalElements":1,"totalPages":1,"page":1,"size":20}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"post":{"tags":["BadgesCategories"],"description":"Crea categoria badge del programma indicato in `programId`.\n\n**Contesto:** Le **Categorie badge** raggruppano achievement/gamification assegnabili ai clienti (fedeltà, visita, milestone).\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n\n**Body JSON:**\n- `name` (string) — obbligatorio\n- `description` (string) — opzionale\n- `status` (string) — opzionale (valori: ENABLED, DISABLED)\n\n**Risposte di successo:**\n- **201**: Risorsa creata (badges-categories)\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** BadgesCategories.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["name"],"properties":{"name":{"type":"string"},"description":{"type":"string"},"status":{"type":"string","enum":["ENABLED","DISABLED"]}}}}},"required":true},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"201":{"description":"Risorsa creata (badges-categories)","content":{"application/json":{"schema":{"description":"Risorsa creata (badges-categories)","type":"object","additionalProperties":true,"example":{"id":1,"programId":1,"name":"Categoria badge","description":null,"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/badges-categories/{id}":{"get":{"tags":["BadgesCategories"],"description":"Dettaglio di una programma fedeltà per identificativo del programma indicato in `programId`.\n\n**Contesto:** Le **Categorie badge** raggruppano achievement/gamification assegnabili ai clienti (fedeltà, visita, milestone).\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n- `id` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Dettaglio badges-categories\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** BadgesCategories.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Dettaglio badges-categories","content":{"application/json":{"schema":{"description":"Dettaglio badges-categories","type":"object","additionalProperties":true,"example":{"id":1,"programId":1,"name":"Categoria badge","description":null,"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"put":{"tags":["BadgesCategories"],"description":"Aggiorna una programma fedeltà (campi consentiti) del programma indicato in `programId`.\n\n**Contesto:** Le **Categorie badge** raggruppano achievement/gamification assegnabili ai clienti (fedeltà, visita, milestone).\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n- `id` (integer) — obbligatorio\n\n**Body JSON:**\n- `name` (string) — opzionale\n- `description` (string) — opzionale\n- `status` (string) — opzionale (valori: ENABLED, DISABLED)\n\n**Risposte di successo:**\n- **200**: Aggiornamento badges-categories\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** BadgesCategories.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"description":{"type":"string"},"status":{"type":"string","enum":["ENABLED","DISABLED"]}}}}}},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Aggiornamento badges-categories","content":{"application/json":{"schema":{"description":"Aggiornamento badges-categories","type":"object","additionalProperties":true,"example":{"id":1,"programId":1,"name":"Categoria badge","description":null,"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"delete":{"tags":["BadgesCategories"],"description":"Elimina una programma fedeltà del programma indicato in `programId` (soft delete o `TERMINATED` se vincoli FK).\n\n**Contesto:** Le **Categorie badge** raggruppano achievement/gamification assegnabili ai clienti (fedeltà, visita, milestone).\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n- `id` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **204**: Operazione riuscita senza corpo di risposta.\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** BadgesCategories.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"204":{"description":"Operazione riuscita senza corpo di risposta."},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/badges":{"get":{"tags":["Badges"],"description":"Elenco paginato di badge del programma indicato in `programId`.\n\n**Contesto:** I **Badge** sono riconoscimenti assegnabili manualmente o via promo. Possono avere livelli e conteggi clienti associati.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n\n**Querystring:**\n- `categoryId` (integer) — opzionale: Filter by badge category\n- `page` (integer) — opzionale [default: 1]\n- `size` (integer) — opzionale [default: 20]\n\n**Risposte di successo:**\n- **200**: Lista badges\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Badges.","parameters":[{"schema":{"type":"integer"},"in":"query","name":"categoryId","required":false,"description":"Filter by badge category"},{"schema":{"type":"integer","default":1},"in":"query","name":"page","required":false},{"schema":{"type":"integer","default":20},"in":"query","name":"size","required":false},{"schema":{"type":"integer"},"in":"path","name":"programId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Lista badges","content":{"application/json":{"schema":{"description":"Lista badges","type":"object","additionalProperties":true,"example":{"content":[{"id":1,"programId":1,"name":"Badge","description":null,"categoryId":1,"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}],"totalElements":1,"totalPages":1,"page":1,"size":20}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"post":{"tags":["Badges"],"description":"Crea badge del programma indicato in `programId`.\n\n**Contesto:** I **Badge** sono riconoscimenti assegnabili manualmente o via promo. Possono avere livelli e conteggi clienti associati.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n\n**Body JSON:**\n- `name` (string) — obbligatorio\n- `description` (string) — opzionale\n- `categoryId` (integer) — obbligatorio\n- `status` (string) — opzionale (valori: ENABLED, DISABLED)\n\n**Risposte di successo:**\n- **201**: Risorsa creata (badges)\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Badges.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["name","categoryId"],"properties":{"name":{"type":"string"},"description":{"type":"string"},"categoryId":{"type":"integer"},"status":{"type":"string","enum":["ENABLED","DISABLED"]}}}}},"required":true},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"201":{"description":"Risorsa creata (badges)","content":{"application/json":{"schema":{"description":"Risorsa creata (badges)","type":"object","additionalProperties":true,"example":{"id":1,"programId":1,"name":"Badge","description":null,"categoryId":1,"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/badges/{badgeId}/customers/count":{"get":{"tags":["Badges"],"description":"Elenco paginato di count del programma indicato in `programId`.\n\n**Contesto:** I **Badge** sono riconoscimenti assegnabili manualmente o via promo. Possono avere livelli e conteggi clienti associati.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n- `badgeId` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Conteggio clienti con badge.\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Badges.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"badgeId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Conteggio clienti con badge.","content":{"application/json":{"schema":{"description":"Conteggio clienti con badge.","type":"object","additionalProperties":true,"example":{"count":42}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/badges/assign":{"post":{"tags":["Badges"],"description":"Assegna un badge a un cliente (stesso programma). Persistenza su `customer_badges`; idempotente se già assegnato.\n\n**Contesto:** I **Badge** sono riconoscimenti assegnabili manualmente o via promo. Possono avere livelli e conteggi clienti associati.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Body JSON:**\n- `customerId` (integer) — obbligatorio\n- `badgeId` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **201**: Badge assegnato (`customerId`, `badgeId`, `assignedAt`, `alreadyAssigned`)\n\n**Errori documentati:** 400, 401, 403, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Badges.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["customerId","badgeId"],"properties":{"customerId":{"type":"integer"},"badgeId":{"type":"integer"}}}}},"required":true},"security":[{"bearerAuth":[]}],"responses":{"201":{"description":"Badge assegnato","content":{"application/json":{"schema":{"description":"Badge assegnato","type":"object","additionalProperties":true,"example":{"customerId":2,"badgeId":1,"assignedAt":"2026-09-04T12:00:00.000Z","alreadyAssigned":false}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/badges/{id}":{"get":{"tags":["Badges"],"description":"Dettaglio di badge per identificativo.\n\n**Contesto:** I **Badge** sono riconoscimenti assegnabili manualmente o via promo. Possono avere livelli e conteggi clienti associati.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `id` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Badge\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Badges.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Badge","content":{"application/json":{"schema":{"description":"Badge","type":"object","additionalProperties":true,"example":{"id":1,"programId":1,"name":"Badge","description":null,"categoryId":1,"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"put":{"tags":["Badges"],"description":"Aggiorna badge (campi consentiti).\n\n**Contesto:** I **Badge** sono riconoscimenti assegnabili manualmente o via promo. Possono avere livelli e conteggi clienti associati.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `id` (integer) — obbligatorio\n\n**Body JSON:**\n- `name` (string) — opzionale\n- `description` (string) — opzionale\n- `categoryId` (integer) — obbligatorio\n- `status` (string) — opzionale (valori: ENABLED, DISABLED)\n\n**Risposte di successo:**\n- **200**: Badge aggiornato\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Badges.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["categoryId"],"properties":{"name":{"type":"string"},"description":{"type":"string"},"categoryId":{"type":"integer"},"status":{"type":"string","enum":["ENABLED","DISABLED"]}}}}},"required":true},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Badge aggiornato","content":{"application/json":{"schema":{"description":"Badge aggiornato","type":"object","additionalProperties":true,"example":{"id":1,"programId":1,"name":"Badge","description":null,"categoryId":1,"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"delete":{"tags":["Badges"],"description":"Elimina badge (soft delete o `TERMINATED` se vincoli FK).\n\n**Contesto:** I **Badge** sono riconoscimenti assegnabili manualmente o via promo. Possono avere livelli e conteggi clienti associati.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `id` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **204**: Operazione riuscita senza corpo di risposta.\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Badges.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"204":{"description":"Operazione riuscita senza corpo di risposta."},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/dynamic-fields":{"get":{"tags":["CustomersFields"],"description":"List dynamic fields. fieldTypeOptions contiene le opzioni per il combobox \"Tipo di campo\" (testo, numerico, boolean, etc.).\n\n**Contesto:** I **Campi dinamici cliente** estendono l'anagrafica con attributi custom (testo, numero, data, elenco). Configurabili per programma e valorizzabili su ogni cliente.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n\n**Querystring:**\n- `enabled` (boolean) — opzionale\n- `visible` (boolean) — opzionale\n- `kind` (integer) — opzionale: 1=CUSTOMER, 2=SHOP, 3=DEALER [default: 1]\n- `page` (integer) — opzionale [default: 1]\n- `size` (integer) — opzionale [default: 20]\n\n**Risposte di successo:**\n- **200**: Lista dynamic-fields\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** CustomersFields.","parameters":[{"schema":{"type":"boolean"},"in":"query","name":"enabled","required":false},{"schema":{"type":"boolean"},"in":"query","name":"visible","required":false},{"schema":{"type":"integer","default":1},"in":"query","name":"kind","required":false,"description":"1=CUSTOMER, 2=SHOP, 3=DEALER"},{"schema":{"type":"integer","default":1},"in":"query","name":"page","required":false},{"schema":{"type":"integer","default":20},"in":"query","name":"size","required":false},{"schema":{"type":"integer"},"in":"path","name":"programId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Lista dynamic-fields","content":{"application/json":{"schema":{"type":"object","properties":{"content":{"type":"array","items":{"type":"object","properties":{"id":{"type":"integer"},"name":{"type":"string"},"fieldType":{"type":"string"},"fieldTypeLabel":{"type":"string"},"minValue":{"type":"number","nullable":true},"maxValue":{"type":"number","nullable":true},"length":{"type":"integer","nullable":true},"fieldTypeRepresentation":{"type":"string","description":"EDIT, MEMO, COMBO, DATE_TIME_PICKER, SPIN, LIST, RADIO_BUTTON, CHECK_BOX, CHECK_BOX_LIST"},"defaultValue":{"type":"string","nullable":true},"mandatory":{"type":"boolean"},"visible":{"type":"boolean"},"enabled":{"type":"boolean"},"options":{"type":"array","items":{"type":"object","properties":{"id":{"type":"integer"},"value":{"type":"string"},"dictionaryKey":{"type":"string","nullable":true},"isDefault":{"type":"boolean"},"order":{"type":"integer","nullable":true}}}}}}},"fieldTypeOptions":{"type":"array","description":"Opzioni per il combobox Tipo di campo (value + label)","items":{"type":"object","properties":{"value":{"type":"string"},"label":{"type":"string"}}}},"totalElements":{"type":"integer"},"totalPages":{"type":"integer"},"page":{"type":"integer"},"size":{"type":"integer"}},"description":"Lista dynamic-fields","example":{"content":[{"id":1,"programId":1,"name":"Campo custom","fieldType":"STRING","kind":1,"mandatory":false,"visible":true,"enabled":true,"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}],"totalElements":1,"totalPages":1,"page":1,"size":20},"additionalProperties":true}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"post":{"tags":["CustomersFields"],"description":"Crea campo dinamico del programma indicato in `programId`.\n\n**Contesto:** I **Campi dinamici cliente** estendono l'anagrafica con attributi custom (testo, numero, data, elenco). Configurabili per programma e valorizzabili su ogni cliente.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n\n**Body JSON:**\n- `name` (string) — obbligatorio\n- `type` (string) — opzionale (valori: TEXT, NUMBER, BOOLEAN, COMBOBOX, DATE)\n- `fieldType` (string) — opzionale (valori: BOOLEAN, DATE, TIME, DATE_TIME, FLOAT, INTEGER, STRING)\n- `fieldTypeRepresentation` (string) — opzionale (valori: EDIT, MEMO, COMBO, DATE_TIME_PICKER, SPIN, LIST, RADIO_BUTTON, CHECK_BOX, CHECK_BOX_LIST)\n- `defaultValue` (string) — opzionale\n- `mandatory` (boolean) — opzionale\n- `visible` (boolean) — opzionale\n- `enabled` (boolean) — opzionale\n- `minValue` (number) — opzionale\n- `maxValue` (number) — opzionale\n- `length` (integer) — opzionale\n- `kind` (integer) — opzionale: 1=CUSTOMER, 2=SHOP, 3=DEALER [default: 1]\n- `options` (array) — opzionale\n\n**Risposte di successo:**\n- **201**: Risorsa creata (dynamic-fields)\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** CustomersFields.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["name"],"properties":{"name":{"type":"string"},"type":{"type":"string","enum":["TEXT","NUMBER","BOOLEAN","COMBOBOX","DATE"]},"fieldType":{"type":"string","enum":["BOOLEAN","DATE","TIME","DATE_TIME","FLOAT","INTEGER","STRING"]},"fieldTypeRepresentation":{"type":"string","enum":["EDIT","MEMO","COMBO","DATE_TIME_PICKER","SPIN","LIST","RADIO_BUTTON","CHECK_BOX","CHECK_BOX_LIST"]},"defaultValue":{"type":"string","nullable":true},"mandatory":{"type":"boolean"},"visible":{"type":"boolean"},"enabled":{"type":"boolean"},"minValue":{"type":"number","nullable":true},"maxValue":{"type":"number","nullable":true},"length":{"type":"integer","nullable":true},"kind":{"type":"integer","default":1,"description":"1=CUSTOMER, 2=SHOP, 3=DEALER"},"options":{"type":"array","items":{"type":"object","required":["value"],"properties":{"value":{"type":"string"},"isDefault":{"type":"boolean","default":false}}}}}}}},"required":true},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"201":{"description":"Risorsa creata (dynamic-fields)","content":{"application/json":{"schema":{"description":"Risorsa creata (dynamic-fields)","type":"object","additionalProperties":true,"example":{"id":1,"programId":1,"name":"Campo custom","fieldType":"STRING","kind":1,"mandatory":false,"visible":true,"enabled":true,"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/dynamic-fields/{fieldId}":{"get":{"tags":["CustomersFields"],"description":"Dettaglio di una programma fedeltà per identificativo del programma indicato in `programId`.\n\n**Contesto:** I **Campi dinamici cliente** estendono l'anagrafica con attributi custom (testo, numero, data, elenco). Configurabili per programma e valorizzabili su ogni cliente.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n- `fieldId` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Dettaglio dynamic-fields\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** CustomersFields.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"fieldId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Dettaglio dynamic-fields","content":{"application/json":{"schema":{"description":"Dettaglio dynamic-fields","type":"object","additionalProperties":true,"example":{"id":1,"programId":1,"name":"Campo custom","fieldType":"STRING","kind":1,"mandatory":false,"visible":true,"enabled":true,"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"put":{"tags":["CustomersFields"],"description":"Aggiorna una programma fedeltà (campi consentiti) del programma indicato in `programId`.\n\n**Contesto:** I **Campi dinamici cliente** estendono l'anagrafica con attributi custom (testo, numero, data, elenco). Configurabili per programma e valorizzabili su ogni cliente.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n- `fieldId` (integer) — obbligatorio\n\n**Body JSON:**\n- `name` (string) — opzionale\n- `fieldType` (string) — opzionale (valori: BOOLEAN, DATE, TIME, DATE_TIME, FLOAT, INTEGER, STRING)\n- `fieldTypeRepresentation` (string) — opzionale (valori: EDIT, MEMO, COMBO, DATE_TIME_PICKER, SPIN, LIST, RADIO_BUTTON, CHECK_BOX, CHECK_BOX_LIST)\n- `defaultValue` (string) — opzionale\n- `mandatory` (boolean) — opzionale\n- `visible` (boolean) — opzionale\n- `enabled` (boolean) — opzionale\n- `minValue` (number) — opzionale\n- `maxValue` (number) — opzionale\n- `length` (integer) — opzionale\n- `options` (array) — opzionale\n\n**Risposte di successo:**\n- **200**: Aggiornamento dynamic-fields\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** CustomersFields.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"fieldType":{"type":"string","enum":["BOOLEAN","DATE","TIME","DATE_TIME","FLOAT","INTEGER","STRING"]},"fieldTypeRepresentation":{"type":"string","enum":["EDIT","MEMO","COMBO","DATE_TIME_PICKER","SPIN","LIST","RADIO_BUTTON","CHECK_BOX","CHECK_BOX_LIST"]},"defaultValue":{"type":"string","nullable":true},"mandatory":{"type":"boolean"},"visible":{"type":"boolean"},"enabled":{"type":"boolean"},"minValue":{"type":"number","nullable":true},"maxValue":{"type":"number","nullable":true},"length":{"type":"integer","nullable":true},"options":{"type":"array","items":{"type":"object","required":["value"],"properties":{"value":{"type":"string"},"isDefault":{"type":"boolean","default":false}}}}}}}}},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"fieldId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Aggiornamento dynamic-fields","content":{"application/json":{"schema":{"description":"Aggiornamento dynamic-fields","type":"object","additionalProperties":true,"example":{"id":1,"programId":1,"name":"Campo custom","fieldType":"STRING","kind":1,"mandatory":false,"visible":true,"enabled":true,"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"delete":{"tags":["CustomersFields"],"description":"Elimina una programma fedeltà del programma indicato in `programId` (soft delete o `TERMINATED` se vincoli FK).\n\n**Contesto:** I **Campi dinamici cliente** estendono l'anagrafica con attributi custom (testo, numero, data, elenco). Configurabili per programma e valorizzabili su ogni cliente.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n- `fieldId` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **204**: Operazione riuscita senza corpo di risposta.\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** CustomersFields.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"fieldId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"204":{"description":"Operazione riuscita senza corpo di risposta."},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/customers/{customerId}/dynamic-fields":{"get":{"tags":["CustomersFields"],"description":"Elenco paginato di campo dinamico.\n\n**Contesto:** I **Campi dinamici cliente** estendono l'anagrafica con attributi custom (testo, numero, data, elenco). Configurabili per programma e valorizzabili su ogni cliente.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `customerId` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Valori campi dinamici cliente\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** CustomersFields.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"customerId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Valori campi dinamici cliente","content":{"application/json":{"schema":{"description":"Valori campi dinamici cliente","type":"object","additionalProperties":true,"example":[{"dynamicFieldId":1,"name":"Colore","value":"blue"}]}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/customers/{customerId}/dynamic-fields/{fieldId}":{"put":{"tags":["CustomersFields"],"description":"Aggiorna campo dinamico (campi consentiti).\n\n**Contesto:** I **Campi dinamici cliente** estendono l'anagrafica con attributi custom (testo, numero, data, elenco). Configurabili per programma e valorizzabili su ogni cliente.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `customerId` (integer) — obbligatorio\n- `fieldId` (integer) — obbligatorio\n\n**Body JSON:**\n- `value` (string) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Valore aggiornato\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** CustomersFields.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["value"],"properties":{"value":{"type":"string","nullable":true}}}}},"required":true},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"customerId","required":true},{"schema":{"type":"integer"},"in":"path","name":"fieldId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Valore aggiornato","content":{"application/json":{"schema":{"description":"Valore aggiornato","type":"object","additionalProperties":true,"example":{"dynamicFieldId":1,"value":"rosso"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/communication-messages":{"get":{"tags":["Communications"],"description":"Elenco paginato di messaggio del programma indicato in `programId`.\n\n**Contesto:** Le **Comunicazioni** inviano messaggi multicanale (email, SMS, push, WhatsApp) a clienti o audience, con stato di invio e template.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n\n**Querystring:**\n- `type` (string) — opzionale (valori: EMAIL, SMS, PUSH, WHATSAPP, RCS)\n- `status` (string) — opzionale\n- `fromDate` (string) — opzionale\n- `toDate` (string) — opzionale\n- `page` (integer) — opzionale [default: 1]\n- `size` (integer) — opzionale [default: 20]\n\n**Risposte di successo:**\n- **200**: Messaggi programma\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Communications.","parameters":[{"schema":{"type":"string","enum":["EMAIL","SMS","PUSH","WHATSAPP","RCS"]},"in":"query","name":"type","required":false},{"schema":{"type":"string"},"in":"query","name":"status","required":false},{"schema":{"type":"string","format":"date"},"in":"query","name":"fromDate","required":false},{"schema":{"type":"string","format":"date"},"in":"query","name":"toDate","required":false},{"schema":{"type":"integer","default":1},"in":"query","name":"page","required":false},{"schema":{"type":"integer","default":20},"in":"query","name":"size","required":false},{"schema":{"type":"integer"},"in":"path","name":"programId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Messaggi programma","content":{"application/json":{"schema":{"description":"Messaggi programma","type":"object","additionalProperties":true,"example":{"content":[{"id":1,"programId":1,"channel":"EMAIL","status":"DRAFT","subject":"Oggetto","createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}],"totalElements":1,"totalPages":1,"page":1,"size":20}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/communication-messages/push-test":{"post":{"tags":["Communications"],"description":"Invio PUSH immediato di test via OneSignal (usa message_providers channel=PUSH del programma).\n\n**Contesto:** Le **Comunicazioni** inviano messaggi multicanale (email, SMS, push, WhatsApp) a clienti o audience, con stato di invio e template.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Body JSON:**\n- `programId` (integer) — obbligatorio\n- `customerId` (integer) — obbligatorio\n- `subject` (string) — opzionale\n- `body` (string) — opzionale\n- `data` (object) — opzionale: Campi custom nel payload data OneSignal\n\n**Risposte di successo:**\n- **200**: Push di test inviato\n\n**Errori documentati:** 400, 401, 403, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Communications.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["programId","customerId"],"properties":{"programId":{"type":"integer"},"customerId":{"type":"integer"},"subject":{"type":"string"},"body":{"type":"string"},"data":{"type":"object","description":"Campi custom nel payload data OneSignal"}}}}},"required":true},"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Push di test inviato","content":{"application/json":{"schema":{"description":"Push di test inviato","type":"object","additionalProperties":true,"example":{"ok":true,"providerCode":"ONESIGNAL","responseStatus":200,"providerTransactionId":"abc123"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/communication-messages/send":{"post":{"tags":["Communications"],"description":"Crea send.\n\n**Contesto:** Le **Comunicazioni** inviano messaggi multicanale (email, SMS, push, WhatsApp) a clienti o audience, con stato di invio e template.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Body JSON:**\n- `customerId` (integer) — obbligatorio\n- `type` (string) — obbligatorio (valori: EMAIL, SMS, PUSH, WHATSAPP, RCS)\n- `templateId` (integer) — opzionale\n- `subject` (string) — opzionale\n- `body` (string) — opzionale\n- `variables` (object) — opzionale\n\n**Risposte di successo:**\n- **200**: Invio accodato\n\n**Errori documentati:** 400, 401, 403, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Communications.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["customerId","type"],"properties":{"customerId":{"type":"integer"},"type":{"type":"string","enum":["EMAIL","SMS","PUSH","WHATSAPP","RCS"]},"templateId":{"type":"integer"},"subject":{"type":"string"},"body":{"type":"string"},"variables":{"type":"object"},"kindId":{"type":"integer","description":"Tipo notifica app (kind OneSignal)"},"objectId":{"type":"integer","description":"Id oggetto correlato (tap in app)"},"mediaUrl":{"type":"string","description":"URL HTTPS di un allegato (ricevuta, PDF). Il job lo scarica e lo allega all'email."}}}}},"required":true},"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Invio accodato","content":{"application/json":{"schema":{"description":"Invio accodato","type":"object","additionalProperties":true,"example":{"sent":true,"messageId":1}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/communication-messages/tags":{"get":{"tags":["Communications"],"description":"List available communication message tags (e.g. #Nome#, #Cognome#). Usabili in subject/body per messaggi promo (azione Msg). Sostituzione case-insensitive. Vedi 32_MESSAGE_TAGS_PROMO.md.\n\n**Contesto:** Le **Comunicazioni** inviano messaggi multicanale (email, SMS, push, WhatsApp) a clienti o audience, con stato di invio e template.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Risposte di successo:**\n- **200**: Tag disponibili\n\n**Errori documentati:** 400, 401, 403, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Communications.","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Tag disponibili","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"tag":{"type":"string","example":"#Nome#"},"description":{"type":"string"},"example":{"type":"string"}}},"description":"Tag disponibili","example":[{"id":1,"name":"Promo"}]}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/communication-messages/{id}":{"get":{"tags":["Communications"],"description":"Dettaglio di messaggio per identificativo.\n\n**Contesto:** Le **Comunicazioni** inviano messaggi multicanale (email, SMS, push, WhatsApp) a clienti o audience, con stato di invio e template.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `id` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Messaggio\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Communications.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Messaggio","content":{"application/json":{"schema":{"description":"Messaggio","type":"object","additionalProperties":true,"example":{"id":1,"programId":1,"channel":"EMAIL","status":"DRAFT","subject":"Oggetto","createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"put":{"tags":["Communications"],"description":"Aggiorna messaggio (campi consentiti).\n\n**Contesto:** Le **Comunicazioni** inviano messaggi multicanale (email, SMS, push, WhatsApp) a clienti o audience, con stato di invio e template.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `id` (integer) — obbligatorio\n\n**Body JSON:**\n- `status` (string) — opzionale\n\n**Risposte di successo:**\n- **200**: Messaggio aggiornato\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Communications.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string"}}}}}},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Messaggio aggiornato","content":{"application/json":{"schema":{"description":"Messaggio aggiornato","type":"object","additionalProperties":true,"example":{"id":1,"programId":1,"channel":"EMAIL","status":"DRAFT","subject":"Oggetto","createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"delete":{"tags":["Communications"],"description":"Delete communication message. Con JWT operatorType=CUSTOMER elimina solo messaggi PUSH del cliente del token (stesso programId).\n\n**Contesto:** Le **Comunicazioni** inviano messaggi multicanale (email, SMS, push, WhatsApp) a clienti o audience, con stato di invio e template.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `id` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **204**: Messaggio eliminato\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Communications.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"204":{"description":"Messaggio eliminato","content":{"application/json":{"schema":{"description":"Messaggio eliminato"}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/communication-templates":{"get":{"tags":["Communications"],"description":"Elenco paginato di template comunicazione del programma indicato in `programId`.\n\n**Contesto:** Le **Comunicazioni** inviano messaggi multicanale (email, SMS, push, WhatsApp) a clienti o audience, con stato di invio e template.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (string) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Elenco template di comunicazione del programma.\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Communications.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"in":"path","name":"programId","required":true}],"responses":{"200":{"description":"Elenco template di comunicazione del programma.","content":{"application/json":{"schema":{"description":"Elenco template di comunicazione del programma.","type":"object","additionalProperties":true,"example":{"content":[{"id":1,"programId":1,"code":"COUPON_GIFT_EXISTING_EMAIL","channel":"EMAIL","name":"Regalo coupon — email cliente esistente","subject":"Hai ricevuto un coupon in regalo","body":"<p>Ciao <strong>#DestName#</strong>, …</p>","active":true,"createdAt":"2026-05-15T10:00:00.000Z","updatedAt":"2026-05-15T10:00:00.000Z"}]}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"post":{"tags":["Communications"],"description":"Crea template comunicazione del programma indicato in `programId`.\n\n**Contesto:** Le **Comunicazioni** inviano messaggi multicanale (email, SMS, push, WhatsApp) a clienti o audience, con stato di invio e template.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (string) — obbligatorio\n\n**Risposte di successo:**\n- **201**: Crea un template di comunicazione (EMAIL, SMS o PUSH).\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Communications.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"in":"path","name":"programId","required":true}],"responses":{"201":{"description":"Crea un template di comunicazione (EMAIL, SMS o PUSH).","content":{"application/json":{"schema":{"description":"Crea un template di comunicazione (EMAIL, SMS o PUSH).","type":"object","additionalProperties":true,"example":{"id":1,"programId":1,"code":"COUPON_GIFT_EXISTING_EMAIL","channel":"EMAIL","name":"Regalo coupon — email cliente esistente","subject":"Hai ricevuto un coupon in regalo","body":"<p>Ciao <strong>#DestName#</strong>, …</p>","active":true,"createdAt":"2026-05-15T10:00:00.000Z","updatedAt":"2026-05-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/communication-templates/{id}":{"get":{"tags":["Communications"],"description":"Dettaglio di una programma fedeltà per identificativo del programma indicato in `programId`.\n\n**Contesto:** Le **Comunicazioni** inviano messaggi multicanale (email, SMS, push, WhatsApp) a clienti o audience, con stato di invio e template.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (string) — obbligatorio\n- `id` (string) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Dettaglio template di comunicazione.\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Communications.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"in":"path","name":"programId","required":true},{"schema":{"type":"string"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Dettaglio template di comunicazione.","content":{"application/json":{"schema":{"description":"Dettaglio template di comunicazione.","type":"object","additionalProperties":true,"example":{"id":1,"programId":1,"code":"COUPON_GIFT_EXISTING_EMAIL","channel":"EMAIL","name":"Regalo coupon — email cliente esistente","subject":"Hai ricevuto un coupon in regalo","body":"<p>Ciao <strong>#DestName#</strong>, …</p>","active":true,"createdAt":"2026-05-15T10:00:00.000Z","updatedAt":"2026-05-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"put":{"tags":["Communications"],"description":"Aggiorna una programma fedeltà (campi consentiti) del programma indicato in `programId`.\n\n**Contesto:** Le **Comunicazioni** inviano messaggi multicanale (email, SMS, push, WhatsApp) a clienti o audience, con stato di invio e template.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (string) — obbligatorio\n- `id` (string) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Aggiorna name, subject, body e active del template.\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Communications.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"in":"path","name":"programId","required":true},{"schema":{"type":"string"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Aggiorna name, subject, body e active del template.","content":{"application/json":{"schema":{"description":"Aggiorna name, subject, body e active del template.","type":"object","additionalProperties":true,"example":{"id":1,"programId":1,"code":"COUPON_GIFT_EXISTING_EMAIL","channel":"EMAIL","name":"Regalo coupon — email cliente esistente","subject":"Hai ricevuto un coupon in regalo","body":"<p>Ciao <strong>#DestName#</strong>, …</p>","active":true,"createdAt":"2026-05-15T10:00:00.000Z","updatedAt":"2026-05-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"delete":{"tags":["Communications"],"description":"Elimina una programma fedeltà del programma indicato in `programId` (soft delete o `TERMINATED` se vincoli FK).\n\n**Contesto:** Le **Comunicazioni** inviano messaggi multicanale (email, SMS, push, WhatsApp) a clienti o audience, con stato di invio e template.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (string) — obbligatorio\n- `id` (string) — obbligatorio\n\n**Risposte di successo:**\n- **204**: Template eliminato.\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Communications.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"in":"path","name":"programId","required":true},{"schema":{"type":"string"},"in":"path","name":"id","required":true}],"responses":{"204":{"description":"Template eliminato."},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/communication-templates/seed-defaults":{"post":{"tags":["Communications"],"description":"Crea seed-defaults del programma indicato in `programId`.\n\n**Contesto:** Le **Comunicazioni** inviano messaggi multicanale (email, SMS, push, WhatsApp) a clienti o audience, con stato di invio e template.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (string) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Inserisce template predefiniti se mancanti e restituisce l'elenco completo.\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Communications.","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"in":"path","name":"programId","required":true}],"responses":{"200":{"description":"Inserisce template predefiniti se mancanti e restituisce l'elenco completo.","content":{"application/json":{"schema":{"description":"Inserisce template predefiniti se mancanti e restituisce l'elenco completo.","type":"object","additionalProperties":true,"example":{"content":[{"id":1,"programId":1,"code":"COUPON_GIFT_EXISTING_EMAIL","channel":"EMAIL","name":"Regalo coupon — email cliente esistente","subject":"Hai ricevuto un coupon in regalo","body":"<p>Ciao <strong>#DestName#</strong>, …</p>","active":true,"createdAt":"2026-05-15T10:00:00.000Z","updatedAt":"2026-05-15T10:00:00.000Z"}]}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/donations":{"get":{"tags":["Donations"],"description":"Elenco paginato di donazione del programma indicato in `programId`.\n\n**Contesto:** Le **Donazioni** trasferiscono unità wallet del cliente verso progetti/ONG solidali configurati nel programma.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n\n**Querystring:**\n- `customerId` (integer) — opzionale\n- `donationOngId` (integer) — opzionale\n- `fromDate` (string) — opzionale\n- `toDate` (string) — opzionale\n- `page` (integer) — opzionale [default: 1]\n- `size` (integer) — opzionale [default: 20]\n\n**Risposte di successo:**\n- **200**: Lista donazioni paginata.\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Donations.","parameters":[{"schema":{"type":"integer"},"in":"query","name":"customerId","required":false},{"schema":{"type":"integer"},"in":"query","name":"donationOngId","required":false},{"schema":{"type":"string","format":"date"},"in":"query","name":"fromDate","required":false},{"schema":{"type":"string","format":"date"},"in":"query","name":"toDate","required":false},{"schema":{"type":"integer","default":1},"in":"query","name":"page","required":false},{"schema":{"type":"integer","default":20},"in":"query","name":"size","required":false},{"schema":{"type":"integer"},"in":"path","name":"programId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Lista donazioni paginata.","content":{"application/json":{"schema":{"description":"Lista donazioni paginata.","type":"object","additionalProperties":true,"example":{"content":[{"id":1,"programId":1,"customerId":2,"donationOngId":1,"donationOngName":"Progetto solidale demo","walletId":1,"amount":5,"currencyId":1,"donationType":"ONE_TIME","sourceMovementId":null,"donationMovementId":10064,"createdAt":"2026-05-15T10:00:00.000Z"}],"totalElements":1,"totalPages":1,"page":1,"size":20}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/donations/{id}":{"get":{"tags":["Donations"],"description":"Dettaglio di donazione per identificativo.\n\n**Contesto:** Le **Donazioni** trasferiscono unità wallet del cliente verso progetti/ONG solidali configurati nel programma.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `id` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Dettaglio donazione.\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Donations.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Dettaglio donazione.","content":{"application/json":{"schema":{"description":"Dettaglio donazione.","type":"object","additionalProperties":true,"example":{"id":1,"programId":1,"customerId":2,"donationOngId":1,"donationOngName":"Progetto solidale demo","walletId":1,"amount":5,"currencyId":1,"donationType":"ONE_TIME","sourceMovementId":null,"donationMovementId":10064,"createdAt":"2026-05-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"put":{"tags":["Donations"],"description":"Aggiorna donazione (campi consentiti).\n\n**Contesto:** Le **Donazioni** trasferiscono unità wallet del cliente verso progetti/ONG solidali configurati nel programma.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `id` (integer) — obbligatorio\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Donations.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"405":{"description":"Le donazioni non sono modificabili.","content":{"application/json":{"schema":{"description":"Le donazioni non sono modificabili.","type":"object","additionalProperties":true,"example":{"error":{"code":"METHOD_NOT_ALLOWED","message":"Donations cannot be updated"}}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"delete":{"tags":["Donations"],"description":"Elimina donazione (soft delete o `TERMINATED` se vincoli FK).\n\n**Contesto:** Le **Donazioni** trasferiscono unità wallet del cliente verso progetti/ONG solidali configurati nel programma.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `id` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **204**: Donazione eliminata (soft-delete, nessun corpo).\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Donations.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"204":{"description":"Donazione eliminata (soft-delete, nessun corpo)."},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/donations":{"post":{"tags":["Donations"],"description":"Crea donazione.\n\n**Contesto:** Le **Donazioni** trasferiscono unità wallet del cliente verso progetti/ONG solidali configurati nel programma.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Body JSON:**\n- `programId` (integer) — obbligatorio\n- `customerId` (integer) — obbligatorio\n- `donationOngId` (integer) — obbligatorio\n- `amount` (number) — obbligatorio\n- `walletId` (integer) — obbligatorio\n- `customerCardId` (integer) — opzionale\n- `currencyId` (integer) — opzionale\n\n**Risposte di successo:**\n- **201**: Donazione registrata.\n\n**Errori documentati:** 400, 401, 403, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Donations.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["programId","customerId","donationOngId","amount","walletId"],"properties":{"programId":{"type":"integer"},"customerId":{"type":"integer"},"donationOngId":{"type":"integer"},"amount":{"type":"number"},"walletId":{"type":"integer"},"customerCardId":{"type":"integer"},"currencyId":{"type":"integer"}}}}},"required":true},"security":[{"bearerAuth":[]}],"responses":{"201":{"description":"Donazione registrata.","content":{"application/json":{"schema":{"description":"Donazione registrata.","type":"object","additionalProperties":true,"example":{"id":1,"programId":1,"customerId":2,"donationOngId":1,"donationOngName":"Progetto solidale demo","walletId":1,"amount":5,"currencyId":1,"donationType":"ONE_TIME","sourceMovementId":null,"donationMovementId":10064,"createdAt":"2026-05-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/open-movements":{"get":{"tags":["OpenMovements"],"description":"Elenco paginato di open movement del programma indicato in `programId`.\n\n**Contesto:** Gli **Open Movements** sono movimenti liberi configurabili (carico/scarico wallet senza vendita classica), con regole e importi variabili.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n\n**Querystring:**\n- `page` (integer) — opzionale [default: 1]\n- `size` (integer) — opzionale [default: 20]\n- `search` (string) — opzionale\n\n**Risposte di successo:**\n- **200**: Lista open-movements\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** OpenMovements.","parameters":[{"schema":{"type":"integer","default":1},"in":"query","name":"page","required":false},{"schema":{"type":"integer","default":20},"in":"query","name":"size","required":false},{"schema":{"type":"string"},"in":"query","name":"search","required":false},{"schema":{"type":"integer"},"in":"path","name":"programId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Lista open-movements","content":{"application/json":{"schema":{"description":"Lista open-movements","type":"object","additionalProperties":true,"example":{"content":[{"id":1,"programId":1,"name":"Open mov","description":null,"valueType":"FIXED","createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}],"totalElements":1,"totalPages":1,"page":1,"size":20}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"post":{"tags":["OpenMovements"],"description":"Crea open movement del programma indicato in `programId`.\n\n**Contesto:** Gli **Open Movements** sono movimenti liberi configurabili (carico/scarico wallet senza vendita classica), con regole e importi variabili.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n\n**Body JSON:**\n- `name` (string) — obbligatorio\n- `description` (string) — opzionale\n- `status` (string) — opzionale (valori: ENABLED, DISABLED)\n\n**Risposte di successo:**\n- **201**: Risorsa creata (open-movements)\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** OpenMovements.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["name"],"properties":{"name":{"type":"string"},"description":{"type":"string"},"status":{"type":"string","enum":["ENABLED","DISABLED"]}}}}},"required":true},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"201":{"description":"Risorsa creata (open-movements)","content":{"application/json":{"schema":{"description":"Risorsa creata (open-movements)","type":"object","additionalProperties":true,"example":{"id":1,"programId":1,"name":"Open mov","description":null,"valueType":"FIXED","createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/open-movements/{id}":{"get":{"tags":["OpenMovements"],"description":"Dettaglio di una programma fedeltà per identificativo del programma indicato in `programId`.\n\n**Contesto:** Gli **Open Movements** sono movimenti liberi configurabili (carico/scarico wallet senza vendita classica), con regole e importi variabili.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n- `id` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Dettaglio open-movements\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** OpenMovements.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Dettaglio open-movements","content":{"application/json":{"schema":{"description":"Dettaglio open-movements","type":"object","additionalProperties":true,"example":{"id":1,"programId":1,"name":"Open mov","description":null,"valueType":"FIXED","createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"put":{"tags":["OpenMovements"],"description":"Aggiorna una programma fedeltà (campi consentiti) del programma indicato in `programId`.\n\n**Contesto:** Gli **Open Movements** sono movimenti liberi configurabili (carico/scarico wallet senza vendita classica), con regole e importi variabili.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n- `id` (integer) — obbligatorio\n\n**Body JSON:**\n- `name` (string) — opzionale\n- `description` (string) — opzionale\n- `status` (string) — opzionale (valori: ENABLED, DISABLED)\n\n**Risposte di successo:**\n- **200**: Aggiornamento open-movements\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** OpenMovements.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"description":{"type":"string"},"status":{"type":"string","enum":["ENABLED","DISABLED"]}}}}}},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Aggiornamento open-movements","content":{"application/json":{"schema":{"description":"Aggiornamento open-movements","type":"object","additionalProperties":true,"example":{"id":1,"programId":1,"name":"Open mov","description":null,"valueType":"FIXED","createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"delete":{"tags":["OpenMovements"],"description":"Elimina una programma fedeltà del programma indicato in `programId` (soft delete o `TERMINATED` se vincoli FK).\n\n**Contesto:** Gli **Open Movements** sono movimenti liberi configurabili (carico/scarico wallet senza vendita classica), con regole e importi variabili.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n- `id` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **204**: Operazione riuscita senza corpo di risposta.\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** OpenMovements.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"204":{"description":"Operazione riuscita senza corpo di risposta."},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/reports/sales":{"get":{"tags":["Reports"],"description":"Elenco paginato di sales.\n\n**Contesto:** I **Report** aggregano KPI operativi e commerciali (vendite, clienti, promo, movimenti) per periodo e programma.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Querystring:**\n- `programId` (integer) — obbligatorio\n- `shopId` (integer) — opzionale\n- `fromDate` (string) — opzionale\n- `toDate` (string) — opzionale\n- `groupBy` (string) — opzionale (valori: day, week, month, shop, category)\n\n**Risposte di successo:**\n- **200**: Report vendite\n\n**Errori documentati:** 400, 401, 403, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Reports.","parameters":[{"schema":{"type":"integer"},"in":"query","name":"programId","required":true},{"schema":{"type":"integer"},"in":"query","name":"shopId","required":false},{"schema":{"type":"string"},"in":"query","name":"fromDate","required":false,"description":"YYYY-MM-DD o ISO"},{"schema":{"type":"string"},"in":"query","name":"toDate","required":false,"description":"YYYY-MM-DD o ISO"},{"schema":{"type":"string","enum":["day","week","month","shop","category"]},"in":"query","name":"groupBy","required":false}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Report vendite","content":{"application/json":{"schema":{"description":"Report vendite","type":"object","additionalProperties":true,"example":{"rows":[{"label":"Totale","value":1234.5,"quantity":42}],"totals":{"amount":1000}}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/reports/customers":{"get":{"tags":["Reports"],"description":"Elenco paginato di cliente.\n\n**Contesto:** I **Report** aggregano KPI operativi e commerciali (vendite, clienti, promo, movimenti) per periodo e programma.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Querystring:**\n- `programId` (integer) — obbligatorio\n- `fromDate` (string) — opzionale\n- `toDate` (string) — opzionale\n- `groupBy` (string) — opzionale\n\n**Risposte di successo:**\n- **200**: Report clienti\n\n**Errori documentati:** 400, 401, 403, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Reports.","parameters":[{"schema":{"type":"integer"},"in":"query","name":"programId","required":true},{"schema":{"type":"string"},"in":"query","name":"fromDate","required":false,"description":"YYYY-MM-DD o ISO"},{"schema":{"type":"string"},"in":"query","name":"toDate","required":false,"description":"YYYY-MM-DD o ISO"},{"schema":{"type":"string"},"in":"query","name":"groupBy","required":false}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Report clienti","content":{"application/json":{"schema":{"description":"Report clienti","type":"object","additionalProperties":true,"example":{"rows":[{"label":"Totale","value":1234.5,"quantity":42}]}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/reports/promotions":{"get":{"tags":["Reports"],"description":"Elenco paginato di promozione.\n\n**Contesto:** I **Report** aggregano KPI operativi e commerciali (vendite, clienti, promo, movimenti) per periodo e programma.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Querystring:**\n- `programId` (integer) — obbligatorio\n- `promotionId` (integer) — opzionale\n- `fromDate` (string) — opzionale\n- `toDate` (string) — opzionale\n\n**Risposte di successo:**\n- **200**: Report promozioni\n\n**Errori documentati:** 400, 401, 403, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Reports.","parameters":[{"schema":{"type":"integer"},"in":"query","name":"programId","required":true},{"schema":{"type":"integer"},"in":"query","name":"promotionId","required":false},{"schema":{"type":"string"},"in":"query","name":"fromDate","required":false,"description":"YYYY-MM-DD o ISO"},{"schema":{"type":"string"},"in":"query","name":"toDate","required":false,"description":"YYYY-MM-DD o ISO"}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Report promozioni","content":{"application/json":{"schema":{"description":"Report promozioni","type":"object","additionalProperties":true,"example":{"rows":[{"label":"Totale","value":1234.5,"quantity":42}]}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/dashboard":{"get":{"tags":["Dashboard"],"description":"Dashboard aggregate stats for a program: shops, terminals, customers, movements, promotions by state","parameters":[{"schema":{"type":"integer"},"in":"query","name":"programId","required":true,"description":"Program id"}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"programId":{"type":"integer"},"shops":{"type":"array","items":{"type":"object","properties":{"id":{"type":["integer","string"]},"nome":{"type":"string"},"qtà":{"type":"integer"}}}},"terminals":{"type":"array","items":{"type":"object","properties":{"id":{"type":["integer","string"]},"nome":{"type":"string"},"qtà":{"type":"integer"}}}},"customers":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"nome":{"type":"string"},"qtà":{"type":"integer"}}}},"wallets":{"type":"array","items":{"type":"object","properties":{"walletId":{"type":"integer"},"walletName":{"type":"string","nullable":true},"cardsCount":{"type":"integer"},"customersCount":{"type":"integer"}}}},"movements":{"type":"object","properties":{"quantity":{"type":"integer","description":"Total number of movements"},"byCurrency":{"type":"array","items":{"type":"object","properties":{"currencyId":{"type":"integer","nullable":true},"currencyName":{"type":"string","nullable":true},"currencySymbol":{"type":"string","nullable":true},"quantity":{"type":"integer"},"amount":{"type":"number"}}}},"byWallet":{"type":"array","items":{"type":"object","properties":{"walletId":{"type":"integer"},"walletName":{"type":"string","nullable":true},"currencySymbol":{"type":"string","nullable":true},"quantity":{"type":"integer","description":"Number of movements involving this wallet"},"chargeUnitsSum":{"type":"number"},"dischargeUnitsSum":{"type":"number"}}}}}},"promotions":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"nome":{"type":"string"},"qtà":{"type":"integer"}}}},"salesByTier":{"type":"array","description":"Sales of the last 3 months grouped by month and tier (only tiers assignable to customers)","items":{"type":"object","properties":{"month":{"type":"string","description":"YYYY-MM"},"tierId":{"type":"integer"},"tierName":{"type":"string"},"tierLevel":{"type":"integer"},"currencySymbol":{"type":"string","nullable":true},"quantity":{"type":"integer","description":"Number of sales"},"amount":{"type":"number","description":"Total sales amount"}}}}},"example":{"programId":0,"shops":[{"id":0,"nome":"","qtà":0}],"terminals":[{"id":0,"nome":"","qtà":0}],"customers":[{"id":"","nome":"","qtà":0}],"wallets":[{"walletId":0,"walletName":"","cardsCount":0,"customersCount":0}],"movements":{"quantity":0,"byCurrency":[{"currencyId":0,"currencyName":"","currencySymbol":"","quantity":0,"amount":0}],"byWallet":[{"walletId":0,"walletName":"","currencySymbol":"","quantity":0,"chargeUnitsSum":0,"dischargeUnitsSum":0}]},"promotions":[{"id":"","nome":"","qtà":0}],"salesByTier":[{"month":"","tierId":0,"tierName":"","tierLevel":0,"currencySymbol":"","quantity":0,"amount":0}]}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/pagatag/dashboard":{"get":{"tags":["Dashboard","PagaTag"],"description":"KPI dashboard PagaTag filtrabili per periodo (dateFrom/dateTo). Serie con granularità automatica (ora/giorno/settimana/mese/anno).","parameters":[{"schema":{"type":"string","format":"date"},"in":"query","name":"dateFrom","required":false},{"schema":{"type":"string","format":"date"},"in":"query","name":"dateTo","required":false},{"schema":{"type":"integer"},"in":"path","name":"programId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/pos-closures":{"get":{"tags":["POS","PagaTag"],"description":"Elenco paginato delle chiusure POS del programma","parameters":[{"schema":{"type":"integer","minimum":1,"default":1},"in":"query","name":"page","required":false},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":20},"in":"query","name":"size","required":false},{"schema":{"type":"string"},"in":"query","name":"dateFrom","required":false},{"schema":{"type":"string"},"in":"query","name":"dateTo","required":false},{"schema":{"type":"integer"},"in":"query","name":"shopId","required":false},{"schema":{"type":"integer"},"in":"query","name":"terminalId","required":false},{"schema":{"type":"integer"},"in":"query","name":"operatorId","required":false},{"schema":{"type":"string"},"in":"query","name":"q","required":false,"description":"Ricerca libera (shop, terminale, operatore, id)"},{"schema":{"type":"integer"},"in":"path","name":"programId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Lista pos-closures","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"description":"Lista pos-closures","example":{"content":[{"id":1,"name":"Esempio","programId":1,"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}],"totalElements":1,"totalPages":1,"page":1,"size":20}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/pos-closures/{id}":{"get":{"tags":["POS","PagaTag"],"description":"Dettaglio chiusura POS (snapshot stampato)","parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Dettaglio pos-closures","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"description":"Dettaglio pos-closures","example":{"id":1,"name":"Esempio","programId":1,"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/movement-types":{"get":{"tags":["Common"],"description":"List all movement types (legacy description + localizedDescription). Endpoint OPEN (no auth).\n\n**Contesto:** Utility **comuni** condivise: dizionario traduzioni, valute, fusi orari e tipi movimento (lookup tables di piattaforma).\n\n**Autenticazione:** endpoint pubblico — non richiede `Authorization: Bearer` (può richiedere `X-API-Key` e/o OTP a seconda del caso).\n\n**Querystring:**\n- `lang` (string) — opzionale: Optional language override. If omitted, backend uses Accept-Language header (or token fallback). (valori: it, en, es)\n- `enabledOnly` (boolean) — opzionale: If true, returns only movement kinds implemented in marta-api 2.0.\n\n**Risposte di successo:**\n- **200**: Tipi movimento\n\n**Errori documentati:** 400, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Common.","parameters":[{"schema":{"type":"string","enum":["it","en","es"]},"in":"query","name":"lang","required":false,"description":"Optional language override. If omitted, backend uses Accept-Language header (or token fallback)."},{"schema":{"type":"boolean"},"in":"query","name":"enabledOnly","required":false,"description":"If true, returns only movement kinds implemented in marta-api 2.0."}],"responses":{"200":{"description":"Tipi movimento","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"kindId":{"type":"integer"},"description":{"type":"string","description":"Legacy description in Italian for backward compatibility"},"localizedDescription":{"type":"string","description":"Description localized using lang/header/token"},"language":{"type":"string","enum":["it","en","es"]},"enabled":{"type":"boolean","description":"True if the movement kind is implemented in marta-api 2.0"},"descriptions":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}}}},"description":"Tipi movimento","example":[{"id":1,"code":"SALE","name":"Vendita"}]}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/dictionary":{"get":{"tags":["Common"],"description":"Elenco paginato di voce dizionario.\n\n**Contesto:** Utility **comuni** condivise: dizionario traduzioni, valute, fusi orari e tipi movimento (lookup tables di piattaforma).\n\n**Autenticazione:** endpoint pubblico — non richiede `Authorization: Bearer` (può richiedere `X-API-Key` e/o OTP a seconda del caso).\n\n**Querystring:**\n- `programId` (integer) — opzionale\n- `language` (string) — opzionale\n\n**Risposte di successo:**\n- **200**: Dizionario label\n\n**Errori documentati:** 400, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Common.","parameters":[{"schema":{"type":"integer"},"in":"query","name":"programId","required":false},{"schema":{"type":"string"},"in":"query","name":"language","required":false}],"responses":{"200":{"description":"Dizionario label","content":{"application/json":{"schema":{"description":"Dizionario label","type":"object","additionalProperties":true,"example":{"key1":"Valore"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/currencies":{"get":{"tags":["Currencies"],"description":"Elenco paginato di valuta.\n\n**Contesto:** Utility **comuni** condivise: dizionario traduzioni, valute, fusi orari e tipi movimento (lookup tables di piattaforma).\n\n**Autenticazione:** endpoint pubblico — non richiede `Authorization: Bearer` (può richiedere `X-API-Key` e/o OTP a seconda del caso).\n\n**Risposte di successo:**\n- **200**: Valute\n\n**Errori documentati:** 400, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Currencies.","responses":{"200":{"description":"Valute","content":{"application/json":{"schema":{"description":"Valute","type":"object","additionalProperties":true,"example":[{"id":1,"code":"EUR","name":"Euro","symbol":"€","decimalPlaces":2,"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}]}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"post":{"tags":["Currencies"],"description":"Crea valuta.\n\n**Contesto:** Utility **comuni** condivise: dizionario traduzioni, valute, fusi orari e tipi movimento (lookup tables di piattaforma).\n\n**Autenticazione:** endpoint pubblico — non richiede `Authorization: Bearer` (può richiedere `X-API-Key` e/o OTP a seconda del caso).\n\n**Body JSON:**\n- `code` (string) — obbligatorio\n- `name` (string) — obbligatorio\n- `symbol` (string) — opzionale\n- `active` (boolean) — opzionale [default: true]\n\n**Risposte di successo:**\n- **201**: Valuta creata\n\n**Errori documentati:** 400, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Currencies.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["code","name"],"properties":{"code":{"type":"string","maxLength":10},"name":{"type":"string"},"symbol":{"type":"string","nullable":true},"active":{"type":"boolean","default":true}}}}},"required":true},"security":[{"bearerAuth":[]}],"responses":{"201":{"description":"Valuta creata","content":{"application/json":{"schema":{"description":"Valuta creata","type":"object","additionalProperties":true,"example":{"id":1,"code":"EUR","name":"Euro","symbol":"€","decimalPlaces":2,"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/time-zones":{"get":{"tags":["Common"],"description":"Elenco paginato di fuso orario.\n\n**Contesto:** Utility **comuni** condivise: dizionario traduzioni, valute, fusi orari e tipi movimento (lookup tables di piattaforma).\n\n**Autenticazione:** endpoint pubblico — non richiede `Authorization: Bearer` (può richiedere `X-API-Key` e/o OTP a seconda del caso).\n\n**Risposte di successo:**\n- **200**: Fusorari\n\n**Errori documentati:** 400, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Common.","responses":{"200":{"description":"Fusorari","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"id":{"type":"integer"},"name":{"type":"string"},"offset":{"type":"number"}}},"description":"Fusorari","example":[{"id":1,"name":"Europe/Rome"}]}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/currencies/{id}":{"get":{"tags":["Currencies"],"description":"Dettaglio di valuta per identificativo.\n\n**Contesto:** Utility **comuni** condivise: dizionario traduzioni, valute, fusi orari e tipi movimento (lookup tables di piattaforma).\n\n**Autenticazione:** endpoint pubblico — non richiede `Authorization: Bearer` (può richiedere `X-API-Key` e/o OTP a seconda del caso).\n\n**Parametri path:**\n- `id` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Valuta\n\n**Errori documentati:** 400, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Currencies.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Valuta","content":{"application/json":{"schema":{"description":"Valuta","type":"object","additionalProperties":true,"example":{"id":1,"code":"EUR","name":"Euro","symbol":"€","decimalPlaces":2,"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"put":{"tags":["Currencies"],"description":"Aggiorna valuta (campi consentiti).\n\n**Contesto:** Utility **comuni** condivise: dizionario traduzioni, valute, fusi orari e tipi movimento (lookup tables di piattaforma).\n\n**Autenticazione:** endpoint pubblico — non richiede `Authorization: Bearer` (può richiedere `X-API-Key` e/o OTP a seconda del caso).\n\n**Parametri path:**\n- `id` (integer) — obbligatorio\n\n**Body JSON:**\n- `code` (string) — opzionale\n- `name` (string) — opzionale\n- `symbol` (string) — opzionale\n- `active` (boolean) — opzionale\n\n**Risposte di successo:**\n- **200**: Valuta aggiornata\n\n**Errori documentati:** 400, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Currencies.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string"},"name":{"type":"string"},"symbol":{"type":"string","nullable":true},"active":{"type":"boolean"}}}}}},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Valuta aggiornata","content":{"application/json":{"schema":{"description":"Valuta aggiornata","type":"object","additionalProperties":true,"example":{"id":1,"code":"EUR","name":"Euro","symbol":"€","decimalPlaces":2,"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"delete":{"tags":["Currencies"],"description":"Elimina valuta (soft delete o `TERMINATED` se vincoli FK).\n\n**Contesto:** Utility **comuni** condivise: dizionario traduzioni, valute, fusi orari e tipi movimento (lookup tables di piattaforma).\n\n**Autenticazione:** endpoint pubblico — non richiede `Authorization: Bearer` (può richiedere `X-API-Key` e/o OTP a seconda del caso).\n\n**Parametri path:**\n- `id` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **204**: Operazione riuscita senza corpo di risposta.\n\n**Errori documentati:** 400, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Currencies.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"204":{"description":"Operazione riuscita senza corpo di risposta."},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/api-keys":{"get":{"tags":["System"],"description":"Elenco paginato di API key.\n\n**Contesto:** Amministrazione **di sistema**: API Key, ticket supporto, audit log e risorse riservate agli operatori di piattaforma.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Querystring:**\n- `programId` (integer) — opzionale\n- `enabled` (boolean) — opzionale\n- `page` (integer) — opzionale [default: 1]\n- `size` (integer) — opzionale [default: 20]\n- `search` (string) — opzionale\n\n**Risposte di successo:**\n- **200**: API keys\n\n**Errori documentati:** 400, 401, 403, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** System.","parameters":[{"schema":{"type":"integer","nullable":true},"in":"query","name":"programId","required":false},{"schema":{"type":"boolean"},"in":"query","name":"enabled","required":false},{"schema":{"type":"integer","default":1},"in":"query","name":"page","required":false},{"schema":{"type":"integer","default":20},"in":"query","name":"size","required":false},{"schema":{"type":"string"},"in":"query","name":"search","required":false},{"schema":{"type":"boolean","default":false},"in":"query","name":"reveal","required":false,"description":"Include apiKey in chiaro (operatori autorizzati)"}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"API keys","content":{"application/json":{"schema":{"description":"API keys","type":"object","additionalProperties":true,"example":{"content":[{"id":1,"name":"Chiave POS","programId":1,"enabled":true,"keyPrefix":"marta_ab","revealable":true,"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}],"totalElements":1,"totalPages":1,"page":1,"size":20}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"post":{"tags":["System"],"description":"Crea API key.\n\n**Contesto:** Amministrazione **di sistema**: API Key, ticket supporto, audit log e risorse riservate agli operatori di piattaforma.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Body JSON:**\n- `name` (string) — obbligatorio\n- `programId` (integer) — opzionale: NULL for SYSTEM level\n- `apiKey` (string) — opzionale: Optional: custom API key. If not provided, generates one\n- `enabled` (boolean) — opzionale [default: true]\n\n**Risposte di successo:**\n- **201**: API key creata (secret solo in creazione)\n\n**Errori documentati:** 400, 401, 403, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** System.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["name"],"properties":{"name":{"type":"string"},"programId":{"type":"integer","description":"NULL for SYSTEM level","nullable":true},"apiKey":{"type":"string","description":"Optional: custom API key. If not provided, generates one","nullable":true},"enabled":{"type":"boolean","default":true}}}}},"required":true},"security":[{"bearerAuth":[]}],"responses":{"201":{"description":"API key creata (secret in chiaro)","content":{"application/json":{"schema":{"description":"API key creata (secret in chiaro)","type":"object","additionalProperties":true,"example":{"id":1,"name":"Chiave POS","programId":1,"enabled":true,"keyPrefix":"marta_ab","revealable":true,"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z","apiKey":"marta_…"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/api-keys/{id}":{"get":{"tags":["System"],"description":"Dettaglio di API key per identificativo.\n\n**Contesto:** Amministrazione **di sistema**: API Key, ticket supporto, audit log e risorse riservate agli operatori di piattaforma.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `id` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **200**: API key\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** System.","parameters":[{"schema":{"type":"boolean","default":false},"in":"query","name":"reveal","required":false},{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"API key (apiKey solo con reveal=true)","content":{"application/json":{"schema":{"description":"API key (apiKey solo con reveal=true)","type":"object","additionalProperties":true,"example":{"id":1,"name":"Chiave POS","programId":1,"enabled":true,"keyPrefix":"marta_ab","revealable":true,"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z","apiKey":"marta_…"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"put":{"tags":["System"],"description":"Aggiorna API key (campi consentiti).\n\n**Contesto:** Amministrazione **di sistema**: API Key, ticket supporto, audit log e risorse riservate agli operatori di piattaforma.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `id` (integer) — obbligatorio\n\n**Body JSON:**\n- `name` (string) — opzionale\n- `enabled` (boolean) — opzionale\n\n**Risposte di successo:**\n- **200**: API key aggiornata\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** System.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"enabled":{"type":"boolean"}}}}}},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"API key aggiornata","content":{"application/json":{"schema":{"description":"API key aggiornata","type":"object","additionalProperties":true,"example":{"id":1,"name":"Chiave POS","programId":1,"enabled":true,"keyPrefix":"marta_ab","revealable":true,"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"delete":{"tags":["System"],"description":"Elimina API key (soft delete o `TERMINATED` se vincoli FK).\n\n**Contesto:** Amministrazione **di sistema**: API Key, ticket supporto, audit log e risorse riservate agli operatori di piattaforma.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `id` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **204**: Operazione riuscita senza corpo di risposta.\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** System.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"204":{"description":"Operazione riuscita senza corpo di risposta."},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/api-keys/{id}/rotate":{"post":{"tags":["System"],"description":"Generate a new secret for an existing API key (old value stops working immediately).","parameters":[{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"API key ruotata (secret in chiaro)","content":{"application/json":{"schema":{"description":"API key ruotata (secret in chiaro)","type":"object","additionalProperties":true,"example":{"id":1,"name":"Chiave POS","programId":1,"enabled":true,"keyPrefix":"marta_ab","revealable":true,"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z","apiKey":"marta_…"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/api-keys/{id}/operators":{"get":{"tags":["System"],"description":"Elenco paginato di operators.\n\n**Contesto:** Amministrazione **di sistema**: API Key, ticket supporto, audit log e risorse riservate agli operatori di piattaforma.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `id` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Operatori collegati\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** System.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Operatori collegati","content":{"application/json":{"schema":{"description":"Operatori collegati","type":"object","additionalProperties":true,"example":[{"operatorId":1,"username":"op"}]}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"post":{"tags":["System"],"description":"Crea operators.\n\n**Contesto:** Amministrazione **di sistema**: API Key, ticket supporto, audit log e risorse riservate agli operatori di piattaforma.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `id` (integer) — obbligatorio\n\n**Body JSON:**\n- `operatorId` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **201**: Collegamento creato\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** System.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["operatorId"],"properties":{"operatorId":{"type":"integer"}}}}},"required":true},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"201":{"description":"Collegamento creato","content":{"application/json":{"schema":{"description":"Collegamento creato","type":"object","additionalProperties":true,"example":{"operatorId":1,"apiKeyId":1}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/api-keys/{id}/operators/{operatorId}":{"delete":{"tags":["System"],"description":"Elimina operators (soft delete o `TERMINATED` se vincoli FK).\n\n**Contesto:** Amministrazione **di sistema**: API Key, ticket supporto, audit log e risorse riservate agli operatori di piattaforma.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `id` (integer) — obbligatorio\n- `operatorId` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **204**: Operazione riuscita senza corpo di risposta.\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** System.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"id","required":true},{"schema":{"type":"integer"},"in":"path","name":"operatorId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"204":{"description":"Operazione riuscita senza corpo di risposta."},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/message-providers":{"get":{"tags":["MessageProviders"],"description":"Elenco paginato di provider messaggi.\n\n**Contesto:** I **Provider messaggi** configurano canali esterni (OneSignal push, SMTP, SMS gateway) per le comunicazioni del programma.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Querystring:**\n- `programId` (integer) — opzionale\n- `channel` (string) — opzionale\n\n**Risposte di successo:**\n- **200**: Provider messaggi\n\n**Errori documentati:** 400, 401, 403, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** MessageProviders.","parameters":[{"schema":{"type":"integer","nullable":true},"in":"query","name":"programId","required":false},{"schema":{"type":"string","nullable":true},"in":"query","name":"channel","required":false}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Provider messaggi","content":{"application/json":{"schema":{"description":"Provider messaggi","type":"object","additionalProperties":true,"example":{"content":[{"id":3,"programId":1,"channel":"PUSH","providerCode":"ONESIGNAL","name":"Push Production","config":{"onesignalAppId":"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx","onesignalApiKey":"onesignal-rest-api-key","baseUrl":"https://onesignal.com/api/v1","defaultFrom":null},"enabled":true,"sandbox":false,"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}],"totalElements":1,"totalPages":1,"page":1,"size":20}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"post":{"tags":["MessageProviders"],"description":"Create message provider (scoped by token.programId if set). Per PUSH/OneSignal usare config.onesignalAppId e config.onesignalApiKey.\n\n**Contesto:** I **Provider messaggi** configurano canali esterni (OneSignal push, SMTP, SMS gateway) per le comunicazioni del programma.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Body JSON:**\n- `programId` (integer) — opzionale\n- `channel` (string) — obbligatorio\n- `providerCode` (string) — obbligatorio\n- `name` (string) — opzionale\n- `config` (object) — opzionale: Configurazione provider. PUSH/OneSignal: { onesignalAppId, onesignalApiKey, baseUrl? }\n- `enabled` (boolean) — opzionale [default: true]\n- `sandbox` (boolean) — opzionale [default: false]\n\n**Risposte di successo:**\n- **201**: Provider creato\n\n**Errori documentati:** 400, 401, 403, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** MessageProviders.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["channel","providerCode"],"properties":{"programId":{"type":"integer","nullable":true},"channel":{"type":"string"},"providerCode":{"type":"string"},"name":{"type":"string","nullable":true},"config":{"type":"object","description":"Configurazione provider. PUSH/OneSignal: { onesignalAppId, onesignalApiKey, baseUrl? }","nullable":true},"enabled":{"type":"boolean","default":true},"sandbox":{"type":"boolean","default":false}}}}},"required":true},"security":[{"bearerAuth":[]}],"responses":{"201":{"description":"Provider creato","content":{"application/json":{"schema":{"description":"Provider creato","type":"object","additionalProperties":true,"example":{"id":3,"programId":1,"channel":"PUSH","providerCode":"ONESIGNAL","name":"Push Production","config":{"onesignalAppId":"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx","onesignalApiKey":"onesignal-rest-api-key","baseUrl":"https://onesignal.com/api/v1","defaultFrom":null},"enabled":true,"sandbox":false,"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/message-providers/{id}":{"get":{"tags":["MessageProviders"],"description":"Dettaglio di provider messaggi per identificativo.\n\n**Contesto:** I **Provider messaggi** configurano canali esterni (OneSignal push, SMTP, SMS gateway) per le comunicazioni del programma.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `id` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Provider\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** MessageProviders.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Provider","content":{"application/json":{"schema":{"description":"Provider","type":"object","additionalProperties":true,"example":{"id":3,"programId":1,"channel":"PUSH","providerCode":"ONESIGNAL","name":"Push Production","config":{"onesignalAppId":"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx","onesignalApiKey":"onesignal-rest-api-key","baseUrl":"https://onesignal.com/api/v1","defaultFrom":null},"enabled":true,"sandbox":false,"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"put":{"tags":["MessageProviders"],"description":"Update message provider (scoped by token.programId if set). PUSH/OneSignal: aggiornare config.onesignalAppId / config.onesignalApiKey.\n\n**Contesto:** I **Provider messaggi** configurano canali esterni (OneSignal push, SMTP, SMS gateway) per le comunicazioni del programma.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `id` (integer) — obbligatorio\n\n**Body JSON:**\n- `channel` (string) — opzionale\n- `providerCode` (string) — opzionale\n- `name` (string) — opzionale\n- `config` (object) — opzionale\n- `enabled` (boolean) — opzionale\n- `sandbox` (boolean) — opzionale\n\n**Risposte di successo:**\n- **200**: Provider aggiornato\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** MessageProviders.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"channel":{"type":"string"},"providerCode":{"type":"string"},"name":{"type":"string","nullable":true},"config":{"type":"object","nullable":true},"enabled":{"type":"boolean"},"sandbox":{"type":"boolean"}}}}}},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Provider aggiornato","content":{"application/json":{"schema":{"description":"Provider aggiornato","type":"object","additionalProperties":true,"example":{"id":3,"programId":1,"channel":"PUSH","providerCode":"ONESIGNAL","name":"Push Production","config":{"onesignalAppId":"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx","onesignalApiKey":"onesignal-rest-api-key","baseUrl":"https://onesignal.com/api/v1","defaultFrom":null},"enabled":true,"sandbox":false,"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"delete":{"tags":["MessageProviders"],"description":"Elimina provider messaggi (soft delete o `TERMINATED` se vincoli FK).\n\n**Contesto:** I **Provider messaggi** configurano canali esterni (OneSignal push, SMTP, SMS gateway) per le comunicazioni del programma.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `id` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **204**: Operazione riuscita senza corpo di risposta.\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** MessageProviders.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"204":{"description":"Operazione riuscita senza corpo di risposta."},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/message-providers/{id}/verify-dns":{"post":{"tags":["MessageProviders"],"security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"in":"path","name":"id","required":true}],"responses":{"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/message-providers/{id}/credits":{"get":{"tags":["MessageProviders"],"security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"in":"path","name":"id","required":true}],"responses":{"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/message-providers/{id}/credits/recharge":{"post":{"tags":["MessageProviders"],"security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"in":"path","name":"id","required":true}],"responses":{"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/message-providers/{id}/credits/unlimited":{"put":{"tags":["MessageProviders"],"security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"in":"path","name":"id","required":true}],"responses":{"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/comunica/overview":{"get":{"tags":["Communications"],"security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"in":"path","name":"programId","required":true}],"responses":{"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/comunica/log":{"get":{"tags":["Communications"],"security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"in":"path","name":"programId","required":true}],"responses":{"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/comunica/log/{messageId}":{"get":{"tags":["Communications"],"security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"in":"path","name":"programId","required":true},{"schema":{"type":"string"},"in":"path","name":"messageId","required":true}],"responses":{"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/comunica/usage":{"get":{"tags":["Communications"],"security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"in":"path","name":"programId","required":true}],"responses":{"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/comunica/test-send":{"post":{"tags":["Communications"],"security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"in":"path","name":"programId","required":true}],"responses":{"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/broadcasts":{"get":{"tags":["Communications"],"description":"Lista broadcasts","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"in":"path","name":"programId","required":true}],"responses":{"200":{"description":"Lista broadcasts","content":{"application/json":{"schema":{"description":"Lista broadcasts","type":"object","additionalProperties":true,"example":{"content":[{"id":1,"name":"Esempio","programId":1,"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}],"totalElements":1,"totalPages":1,"page":1,"size":20}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"post":{"tags":["Communications"],"description":"Risorsa creata (broadcasts)","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"in":"path","name":"programId","required":true}],"responses":{"201":{"description":"Risorsa creata (broadcasts)","content":{"application/json":{"schema":{"description":"Risorsa creata (broadcasts)","type":"object","additionalProperties":true,"example":{"id":1,"name":"Esempio","programId":1,"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/broadcasts/{id}/decision":{"post":{"tags":["Communications"],"security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"in":"path","name":"programId","required":true},{"schema":{"type":"string"},"in":"path","name":"id","required":true}],"responses":{"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/broadcasts/{id}/start":{"post":{"tags":["Communications"],"security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"in":"path","name":"programId","required":true},{"schema":{"type":"string"},"in":"path","name":"id","required":true}],"responses":{"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/broadcasts/{id}/pause":{"post":{"tags":["Communications"],"security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"in":"path","name":"programId","required":true},{"schema":{"type":"string"},"in":"path","name":"id","required":true}],"responses":{"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/broadcasts/{id}/cancel":{"post":{"tags":["Communications"],"security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"in":"path","name":"programId","required":true},{"schema":{"type":"string"},"in":"path","name":"id","required":true}],"responses":{"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/comunica/suppressions":{"get":{"tags":["Communications"],"security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"in":"path","name":"programId","required":true}],"responses":{"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"post":{"tags":["Communications"],"security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"in":"path","name":"programId","required":true}],"responses":{"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/public/email-open/{token}":{"get":{"parameters":[{"schema":{"type":"string"},"in":"path","name":"token","required":true}],"responses":{"200":{"description":"Default Response"}}}},"/api/v1/public/email-preferences/{token}":{"get":{"parameters":[{"schema":{"type":"string"},"in":"path","name":"token","required":true}],"responses":{"200":{"description":"Default Response"}}},"post":{"parameters":[{"schema":{"type":"string"},"in":"path","name":"token","required":true}],"responses":{"200":{"description":"Default Response"}}}},"/api/v1/programs/{programId}/antifraud/rules":{"get":{"tags":["Antifraud"],"description":"Elenco paginato di rules del programma indicato in `programId`.\n\n**Contesto:** Il motore **Antifraud** valuta movimenti sospetti, applica blocchi, bypass temporanei e gestisce investigazioni con stati movimento dedicati.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n\n**Querystring:**\n- `active` (boolean) — opzionale\n- `scope` (string) — opzionale (valori: customer, operator, terminal, shop)\n- `triggerType` (string) — opzionale (valori: purchase, load_points, unload_points, refund)\n- `page` (integer) — opzionale [default: 1]\n- `size` (integer) — opzionale [default: 20]\n\n**Risposte di successo:**\n- **200**: Regole antifraud\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Antifraud.","parameters":[{"schema":{"type":"boolean"},"in":"query","name":"active","required":false},{"schema":{"type":"string","enum":["customer","operator","terminal","shop"]},"in":"query","name":"scope","required":false},{"schema":{"type":"string","enum":["purchase","load_points","unload_points","refund"]},"in":"query","name":"triggerType","required":false},{"schema":{"type":"integer","minimum":1,"default":1},"in":"query","name":"page","required":false},{"schema":{"type":"integer","minimum":1,"maximum":200,"default":20},"in":"query","name":"size","required":false},{"schema":{"type":"integer"},"in":"path","name":"programId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Regole antifraud","content":{"application/json":{"schema":{"description":"Regole antifraud","type":"object","additionalProperties":true,"example":{"content":[{"id":1,"programId":1,"name":"Regola","ruleType":"THRESHOLD","enabled":true,"priority":10,"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}],"totalElements":1,"totalPages":1,"page":1,"size":20}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"post":{"tags":["Antifraud"],"description":"Crea rules del programma indicato in `programId`.\n\n**Contesto:** Il motore **Antifraud** valuta movimenti sospetti, applica blocchi, bypass temporanei e gestisce investigazioni con stati movimento dedicati.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n\n**Body JSON:**\n- `name` (string) — obbligatorio\n- `description` (string) — opzionale\n- `scope` (string) — obbligatorio (valori: customer, operator, terminal, shop)\n- `triggerType` (string) — obbligatorio (valori: purchase, load_points, unload_points, refund)\n- `active` (boolean) — opzionale [default: true]\n- `priority` (integer) — opzionale [default: 0]\n- `conditionsConfig` (object) — obbligatorio\n- `actionsConfig` (object) — obbligatorio\n\n**Risposte di successo:**\n- **201**: Regola creata\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Antifraud.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["name","scope","triggerType","conditionsConfig","actionsConfig"],"properties":{"name":{"type":"string"},"description":{"type":"string","nullable":true},"scope":{"type":"string","enum":["customer","operator","terminal","shop"]},"triggerType":{"type":"string","enum":["purchase","load_points","unload_points","refund"]},"active":{"type":"boolean","default":true},"priority":{"type":"integer","default":0},"conditionsConfig":{"type":"object"},"actionsConfig":{"type":"object"}}}}},"required":true},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"201":{"description":"Regola creata","content":{"application/json":{"schema":{"description":"Regola creata","type":"object","additionalProperties":true,"example":{"id":1,"programId":1,"name":"Regola","ruleType":"THRESHOLD","enabled":true,"priority":10,"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/antifraud/rules/{ruleId}":{"get":{"tags":["Antifraud"],"description":"Dettaglio di una programma fedeltà per identificativo del programma indicato in `programId`.\n\n**Contesto:** Il motore **Antifraud** valuta movimenti sospetti, applica blocchi, bypass temporanei e gestisce investigazioni con stati movimento dedicati.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n- `ruleId` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Dettaglio regola\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Antifraud.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"ruleId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Dettaglio regola","content":{"application/json":{"schema":{"description":"Dettaglio regola","type":"object","additionalProperties":true,"example":{"id":1,"programId":1,"name":"Regola","ruleType":"THRESHOLD","enabled":true,"priority":10,"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"put":{"tags":["Antifraud"],"description":"Aggiorna una programma fedeltà (campi consentiti) del programma indicato in `programId`.\n\n**Contesto:** Il motore **Antifraud** valuta movimenti sospetti, applica blocchi, bypass temporanei e gestisce investigazioni con stati movimento dedicati.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n- `ruleId` (integer) — obbligatorio\n\n**Body JSON:**\n- `name` (string) — obbligatorio\n- `description` (string) — opzionale\n- `scope` (string) — obbligatorio (valori: customer, operator, terminal, shop)\n- `triggerType` (string) — obbligatorio (valori: purchase, load_points, unload_points, refund)\n- `active` (boolean) — opzionale\n- `priority` (integer) — opzionale\n- `conditionsConfig` (object) — obbligatorio\n- `actionsConfig` (object) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Regola aggiornata\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Antifraud.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["name","scope","triggerType","conditionsConfig","actionsConfig"],"properties":{"name":{"type":"string"},"description":{"type":"string","nullable":true},"scope":{"type":"string","enum":["customer","operator","terminal","shop"]},"triggerType":{"type":"string","enum":["purchase","load_points","unload_points","refund"]},"active":{"type":"boolean"},"priority":{"type":"integer"},"conditionsConfig":{"type":"object"},"actionsConfig":{"type":"object"}}}}},"required":true},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"ruleId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Regola aggiornata","content":{"application/json":{"schema":{"description":"Regola aggiornata","type":"object","additionalProperties":true,"example":{"id":1,"programId":1,"name":"Regola","ruleType":"THRESHOLD","enabled":true,"priority":10,"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"delete":{"tags":["Antifraud"],"description":"Elimina una programma fedeltà del programma indicato in `programId` (soft delete o `TERMINATED` se vincoli FK).\n\n**Contesto:** Il motore **Antifraud** valuta movimenti sospetti, applica blocchi, bypass temporanei e gestisce investigazioni con stati movimento dedicati.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n- `ruleId` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **204**: Operazione riuscita senza corpo di risposta.\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Antifraud.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"ruleId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"204":{"description":"Operazione riuscita senza corpo di risposta."},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/antifraud/rules/{ruleId}/status":{"patch":{"tags":["Antifraud"],"description":"Aggiorna parzialmente una programma fedeltà del programma indicato in `programId`.\n\n**Contesto:** Il motore **Antifraud** valuta movimenti sospetti, applica blocchi, bypass temporanei e gestisce investigazioni con stati movimento dedicati.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n- `ruleId` (integer) — obbligatorio\n\n**Body JSON:**\n- `active` (boolean) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Stato regola\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Antifraud.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["active"],"properties":{"active":{"type":"boolean"}}}}},"required":true},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"ruleId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Stato regola","content":{"application/json":{"schema":{"description":"Stato regola","type":"object","additionalProperties":true,"example":{"id":1,"status":"DISABLED"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/antifraud/bypasses":{"get":{"tags":["Antifraud"],"description":"Elenco paginato di bypasses del programma indicato in `programId`.\n\n**Contesto:** Il motore **Antifraud** valuta movimenti sospetti, applica blocchi, bypass temporanei e gestisce investigazioni con stati movimento dedicati.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n\n**Querystring:**\n- `movementId` (integer) — opzionale\n- `scopeType` (string) — opzionale (valori: customer, operator, terminal, shop)\n- `scopeId` (integer) — opzionale\n- `active` (boolean) — opzionale\n- `page` (integer) — opzionale [default: 1]\n- `size` (integer) — opzionale [default: 20]\n\n**Risposte di successo:**\n- **200**: Bypass\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Antifraud.","parameters":[{"schema":{"type":"integer"},"in":"query","name":"movementId","required":false},{"schema":{"type":"string","enum":["customer","operator","terminal","shop"]},"in":"query","name":"scopeType","required":false},{"schema":{"type":"integer"},"in":"query","name":"scopeId","required":false},{"schema":{"type":"boolean"},"in":"query","name":"active","required":false},{"schema":{"type":"integer","minimum":1,"default":1},"in":"query","name":"page","required":false},{"schema":{"type":"integer","minimum":1,"maximum":200,"default":20},"in":"query","name":"size","required":false},{"schema":{"type":"integer"},"in":"path","name":"programId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Bypass","content":{"application/json":{"schema":{"description":"Bypass","type":"object","additionalProperties":true,"example":{"content":[{"id":1,"programId":1,"reason":"Manuale","expiresAt":null,"createdAt":"2026-01-15T10:00:00.000Z"}],"totalElements":1,"totalPages":1,"page":1,"size":20}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"post":{"tags":["Antifraud"],"description":"Crea bypasses del programma indicato in `programId`.\n\n**Contesto:** Il motore **Antifraud** valuta movimenti sospetti, applica blocchi, bypass temporanei e gestisce investigazioni con stati movimento dedicati.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n\n**Body JSON:**\n- `scopeType` (string) — obbligatorio (valori: customer, operator, terminal, shop)\n- `scopeId` (integer) — obbligatorio\n- `reason` (string) — opzionale\n- `active` (boolean) — opzionale [default: true]\n\n**Risposte di successo:**\n- **201**: Bypass creato\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Antifraud.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["scopeType","scopeId"],"properties":{"scopeType":{"type":"string","enum":["customer","operator","terminal","shop"]},"scopeId":{"type":"integer"},"reason":{"type":"string","nullable":true},"active":{"type":"boolean","default":true}}}}},"required":true},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"201":{"description":"Bypass creato","content":{"application/json":{"schema":{"description":"Bypass creato","type":"object","additionalProperties":true,"example":{"id":1,"programId":1,"reason":"Manuale","expiresAt":null,"createdAt":"2026-01-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/antifraud/bypasses/{bypassId}":{"get":{"tags":["Antifraud"],"description":"Dettaglio di una programma fedeltà per identificativo del programma indicato in `programId`.\n\n**Contesto:** Il motore **Antifraud** valuta movimenti sospetti, applica blocchi, bypass temporanei e gestisce investigazioni con stati movimento dedicati.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n- `bypassId` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Dettaglio bypass\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Antifraud.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"bypassId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Dettaglio bypass","content":{"application/json":{"schema":{"description":"Dettaglio bypass","type":"object","additionalProperties":true,"example":{"id":1,"programId":1,"reason":"Manuale","expiresAt":null,"createdAt":"2026-01-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"put":{"tags":["Antifraud"],"description":"Aggiorna una programma fedeltà (campi consentiti) del programma indicato in `programId`.\n\n**Contesto:** Il motore **Antifraud** valuta movimenti sospetti, applica blocchi, bypass temporanei e gestisce investigazioni con stati movimento dedicati.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n- `bypassId` (integer) — obbligatorio\n\n**Body JSON:**\n- `scopeType` (string) — obbligatorio (valori: customer, operator, terminal, shop)\n- `scopeId` (integer) — obbligatorio\n- `reason` (string) — opzionale\n- `active` (boolean) — opzionale\n\n**Risposte di successo:**\n- **200**: Bypass aggiornato\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Antifraud.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["scopeType","scopeId"],"properties":{"scopeType":{"type":"string","enum":["customer","operator","terminal","shop"]},"scopeId":{"type":"integer"},"reason":{"type":"string","nullable":true},"active":{"type":"boolean"}}}}},"required":true},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"bypassId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Bypass aggiornato","content":{"application/json":{"schema":{"description":"Bypass aggiornato","type":"object","additionalProperties":true,"example":{"id":1,"programId":1,"reason":"Manuale","expiresAt":null,"createdAt":"2026-01-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"delete":{"tags":["Antifraud"],"description":"Elimina una programma fedeltà del programma indicato in `programId` (soft delete o `TERMINATED` se vincoli FK).\n\n**Contesto:** Il motore **Antifraud** valuta movimenti sospetti, applica blocchi, bypass temporanei e gestisce investigazioni con stati movimento dedicati.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n- `bypassId` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **204**: Operazione riuscita senza corpo di risposta.\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Antifraud.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"bypassId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"204":{"description":"Operazione riuscita senza corpo di risposta."},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/antifraud/evaluate":{"post":{"tags":["Antifraud"],"description":"Crea evaluate.\n\n**Contesto:** Il motore **Antifraud** valuta movimenti sospetti, applica blocchi, bypass temporanei e gestisce investigazioni con stati movimento dedicati.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Body JSON:**\n- `programId` (integer) — obbligatorio\n- `eventType` (string) — obbligatorio (valori: purchase, load_points, unload_points, refund)\n- `eventId` (integer) — opzionale\n- `eventTimestamp` (string) — opzionale\n- `scopeType` (string) — obbligatorio (valori: customer, operator, terminal, shop)\n- `scopeId` (integer) — obbligatorio\n- `eventData` (object) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Valutazione richiesta\n\n**Errori documentati:** 400, 401, 403, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Antifraud.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["programId","eventType","scopeType","scopeId","eventData"],"properties":{"programId":{"type":"integer"},"eventType":{"type":"string","enum":["purchase","load_points","unload_points","refund"]},"eventId":{"type":"integer","nullable":true},"eventTimestamp":{"type":"string","format":"date-time"},"scopeType":{"type":"string","enum":["customer","operator","terminal","shop"]},"scopeId":{"type":"integer"},"eventData":{"type":"object"}}}}},"required":true},"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Valutazione richiesta","content":{"application/json":{"schema":{"description":"Valutazione richiesta","type":"object","additionalProperties":true,"example":{"decision":"ALLOW","rulesMatched":[]}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/antifraud/logs":{"get":{"tags":["Antifraud"],"description":"Elenco paginato di logs del programma indicato in `programId`.\n\n**Contesto:** Il motore **Antifraud** valuta movimenti sospetti, applica blocchi, bypass temporanei e gestisce investigazioni con stati movimento dedicati.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n\n**Querystring:**\n- `scopeType` (string) — opzionale (valori: customer, operator, terminal, shop)\n- `scopeId` (integer) — opzionale\n- `eventType` (string) — opzionale (valori: purchase, load_points, unload_points, refund)\n- `result` (string) — opzionale (valori: APPROVED, DECLINED, INVESTIGATE, ERROR)\n- `ruleId` (integer) — opzionale\n- `fromDate` (string) — opzionale\n- `toDate` (string) — opzionale\n- `page` (integer) — opzionale [default: 1]\n- `size` (integer) — opzionale [default: 20]\n\n**Risposte di successo:**\n- **200**: Log valutazioni\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Antifraud.","parameters":[{"schema":{"type":"string","enum":["customer","operator","terminal","shop"]},"in":"query","name":"scopeType","required":false},{"schema":{"type":"integer"},"in":"query","name":"scopeId","required":false},{"schema":{"type":"string","enum":["purchase","load_points","unload_points","refund"]},"in":"query","name":"eventType","required":false},{"schema":{"type":"string","enum":["APPROVED","DECLINED","INVESTIGATE","ERROR"]},"in":"query","name":"result","required":false},{"schema":{"type":"integer"},"in":"query","name":"ruleId","required":false},{"schema":{"type":"string","format":"date-time"},"in":"query","name":"fromDate","required":false},{"schema":{"type":"string","format":"date-time"},"in":"query","name":"toDate","required":false},{"schema":{"type":"integer","minimum":1,"default":1},"in":"query","name":"page","required":false},{"schema":{"type":"integer","minimum":1,"maximum":200,"default":20},"in":"query","name":"size","required":false},{"schema":{"type":"integer"},"in":"path","name":"programId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Log valutazioni","content":{"application/json":{"schema":{"description":"Log valutazioni","type":"object","additionalProperties":true,"example":{"content":[{"id":1,"ruleId":1,"movementId":1001,"outcome":"BLOCK","createdAt":"2026-01-15T10:00:00.000Z"}],"totalElements":1,"totalPages":1,"page":1,"size":50}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/antifraud/stats":{"get":{"tags":["Antifraud"],"description":"Elenco paginato di stats del programma indicato in `programId`.\n\n**Contesto:** Il motore **Antifraud** valuta movimenti sospetti, applica blocchi, bypass temporanei e gestisce investigazioni con stati movimento dedicati.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n\n**Querystring:**\n- `fromDate` (string) — opzionale\n- `toDate` (string) — opzionale\n\n**Risposte di successo:**\n- **200**: Statistiche antifraud\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Antifraud.","parameters":[{"schema":{"type":"string","format":"date-time"},"in":"query","name":"fromDate","required":false},{"schema":{"type":"string","format":"date-time"},"in":"query","name":"toDate","required":false},{"schema":{"type":"integer"},"in":"path","name":"programId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Statistiche antifraud","content":{"application/json":{"schema":{"description":"Statistiche antifraud","type":"object","additionalProperties":true,"example":{"blocksLast24h":2,"investigationsOpen":1}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/antifraud/blocks":{"get":{"tags":["Antifraud"],"description":"Elenco paginato di blocks del programma indicato in `programId`.\n\n**Contesto:** Il motore **Antifraud** valuta movimenti sospetti, applica blocchi, bypass temporanei e gestisce investigazioni con stati movimento dedicati.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n\n**Querystring:**\n- `scopeType` (string) — opzionale (valori: customer, operator, terminal, shop)\n- `scopeId` (integer) — opzionale\n- `active` (boolean) — opzionale [default: true]\n\n**Risposte di successo:**\n- **200**: Blocchi attivi\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Antifraud.","parameters":[{"schema":{"type":"string","enum":["customer","operator","terminal","shop"]},"in":"query","name":"scopeType","required":false},{"schema":{"type":"integer"},"in":"query","name":"scopeId","required":false},{"schema":{"type":"boolean","default":true},"in":"query","name":"active","required":false},{"schema":{"type":"integer"},"in":"path","name":"programId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Blocchi attivi","content":{"application/json":{"schema":{"description":"Blocchi attivi","type":"object","additionalProperties":true,"example":{"content":[{"id":1,"programId":1,"scopeType":"CUSTOMER","scopeId":2,"active":true,"createdAt":"2026-01-15T10:00:00.000Z"}],"totalElements":1,"totalPages":1,"page":1,"size":20}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/antifraud/investigations":{"get":{"tags":["Antifraud"],"description":"Elenco paginato di investigations del programma indicato in `programId`.\n\n**Contesto:** Il motore **Antifraud** valuta movimenti sospetti, applica blocchi, bypass temporanei e gestisce investigazioni con stati movimento dedicati.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n\n**Querystring:**\n- `status` (string) — opzionale (valori: PENDING, APPROVED, REFUSED)\n- `movementId` (integer) — opzionale\n- `ruleId` (integer) — opzionale\n- `page` (integer) — opzionale [default: 1]\n- `size` (integer) — opzionale [default: 20]\n\n**Risposte di successo:**\n- **200**: Investigazioni\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Antifraud.","parameters":[{"schema":{"type":"string","enum":["PENDING","APPROVED","REFUSED"]},"in":"query","name":"status","required":false},{"schema":{"type":"integer"},"in":"query","name":"movementId","required":false},{"schema":{"type":"integer"},"in":"query","name":"ruleId","required":false},{"schema":{"type":"integer","minimum":1,"default":1},"in":"query","name":"page","required":false},{"schema":{"type":"integer","minimum":1,"maximum":200,"default":20},"in":"query","name":"size","required":false},{"schema":{"type":"integer"},"in":"path","name":"programId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Investigazioni","content":{"application/json":{"schema":{"description":"Investigazioni","type":"object","additionalProperties":true,"example":{"content":[{"id":1,"programId":1,"status":"OPEN","movementId":1001,"createdAt":"2026-01-15T10:00:00.000Z"}],"totalElements":1,"totalPages":1,"page":1,"size":20}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/antifraud/investigations/{investigationId}/resolve":{"post":{"tags":["Antifraud"],"description":"Crea resolve del programma indicato in `programId`.\n\n**Contesto:** Il motore **Antifraud** valuta movimenti sospetti, applica blocchi, bypass temporanei e gestisce investigazioni con stati movimento dedicati.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n- `investigationId` (integer) — obbligatorio\n\n**Body JSON:**\n- `action` (string) — obbligatorio (valori: APPROVE, REFUSE)\n- `reason` (string) — opzionale\n\n**Risposte di successo:**\n- **200**: Investigazione risolta\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Antifraud.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["action"],"properties":{"action":{"type":"string","enum":["APPROVE","REFUSE"]},"reason":{"type":"string"}}}}},"required":true},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"investigationId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Investigazione risolta","content":{"application/json":{"schema":{"description":"Investigazione risolta","type":"object","additionalProperties":true,"example":{"investigationId":1,"status":"RESOLVED"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/antifraud/blocks/{blockId}/unblock":{"post":{"tags":["Antifraud"],"description":"Sblocca un cliente precedentemente bloccato.\n\n**Contesto:** Il motore **Antifraud** valuta movimenti sospetti, applica blocchi, bypass temporanei e gestisce investigazioni con stati movimento dedicati.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n- `blockId` (integer) — obbligatorio\n\n**Body JSON:**\n- `reason` (string) — opzionale\n- `operatorId` (integer) — opzionale\n\n**Risposte di successo:**\n- **200**: Sblocca un blocco antifraud.\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Antifraud.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"reason":{"type":"string"},"operatorId":{"type":"integer"}}}}}},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"blockId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Sblocca un blocco antifraud.","content":{"application/json":{"schema":{"description":"Sblocca un blocco antifraud.","type":"object","additionalProperties":true,"example":{"success":true}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/movements/{eventId}/status":{"get":{"tags":["Antifraud"],"description":"Elenco paginato di status.\n\n**Contesto:** Il motore **Antifraud** valuta movimenti sospetti, applica blocchi, bypass temporanei e gestisce investigazioni con stati movimento dedicati.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `eventId` (integer) — obbligatorio\n\n**Querystring:**\n- `programId` (integer) — opzionale\n\n**Risposte di successo:**\n- **200**: Stato elaborazione async\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Antifraud.","parameters":[{"schema":{"type":"integer"},"in":"query","name":"programId","required":false},{"schema":{"type":"integer"},"in":"path","name":"eventId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Stato elaborazione async","content":{"application/json":{"schema":{"description":"Stato elaborazione async","type":"object","additionalProperties":true,"example":{"eventId":"uuid","status":"PROCESSED","movementId":1001}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/antifraud/history":{"post":{"tags":["Antifraud"],"description":"Applica le regole attive alle vendite già registrate e apre una revisione. Non modifica punti e non blocca.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"months":{"type":"integer","minimum":1,"maximum":24},"from":{"type":"string"},"to":{"type":"string"}}}}}},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/calculated-values":{"get":{"tags":["Calculated Values"],"description":"Elenco paginato di valore calcolato del programma indicato in `programId`.\n\n**Contesto:** I **Valori calcolati** sono metriche derivate per cliente (es. LTV) ricalcolate periodicamente da regole di business.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n\n**Querystring:**\n- `status` (string) — opzionale (valori: ENABLED, DISABLED)\n- `metric` (string) — opzionale (valori: total_spent, average_ticket, average_monthly_annualized, purchase_count, wallet_charge, wallet_discharge, used_vouchers, open_movements)\n- `page` (integer) — opzionale [default: 1]\n- `size` (integer) — opzionale [default: 20]\n\n**Risposte di successo:**\n- **200**: Lista calculated-values\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Calculated Values.","parameters":[{"schema":{"type":"string","enum":["ENABLED","DISABLED"]},"in":"query","name":"status","required":false},{"schema":{"type":"string","enum":["total_spent","average_ticket","average_monthly_annualized","purchase_count","wallet_charge","wallet_discharge","used_vouchers","open_movements"]},"in":"query","name":"metric","required":false},{"schema":{"type":"integer","minimum":1,"default":1},"in":"query","name":"page","required":false},{"schema":{"type":"integer","minimum":1,"maximum":500,"default":20},"in":"query","name":"size","required":false},{"schema":{"type":"integer"},"in":"path","name":"programId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Lista calculated-values","content":{"application/json":{"schema":{"description":"Lista calculated-values","type":"object","additionalProperties":true,"example":{"content":[{"id":1,"programId":1,"code":"LTV","name":"Lifetime value","resultType":"NUMBER","lastComputedAt":"2026-01-15T10:00:00.000Z","createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}],"totalElements":1,"totalPages":1,"page":1,"size":20}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"post":{"tags":["Calculated Values"],"description":"Crea valore calcolato del programma indicato in `programId`.\n\n**Contesto:** I **Valori calcolati** sono metriche derivate per cliente (es. LTV) ricalcolate periodicamente da regole di business.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n\n**Body JSON:**\n- `name` (string) — obbligatorio\n- `description` (string) — opzionale\n- `dataSource` (object) — obbligatorio\n- `subjects` (string) — obbligatorio (valori: ANY_CUSTOMER, SAME_CUSTOMER)\n- `shopScope` (string) — opzionale (valori: ANY_SHOP, SAME_SHOP) [default: \"ANY_SHOP\"]\n- `where` (object) — opzionale\n- `time` (object) — opzionale\n- `status` (string) — opzionale (valori: ENABLED, DISABLED)\n\n**Risposte di successo:**\n- **201**: Risorsa creata (calculated-values)\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Calculated Values.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["name","dataSource","subjects"],"properties":{"name":{"type":"string","minLength":1,"maxLength":200},"description":{"anyOf":[{"type":"string"},{"type":"null"}]},"dataSource":{"type":"object","required":["type"],"properties":{"type":{"type":"string","enum":["total_spent","average_ticket","average_monthly_annualized","purchase_count","wallet_charge","wallet_discharge","used_vouchers","open_movements"]},"movementKind":{"type":"integer","minimum":1},"walletId":{"type":"integer","minimum":1},"applyMedianFloor":{"type":"boolean"},"medianFloorValue":{"type":"number","minimum":0}}},"subjects":{"type":"string","enum":["ANY_CUSTOMER","SAME_CUSTOMER"]},"shopScope":{"type":"string","enum":["ANY_SHOP","SAME_SHOP"],"default":"ANY_SHOP"},"where":{"type":"object"},"time":{"type":"object"},"status":{"type":"string","enum":["ENABLED","DISABLED"]}}}}},"required":true},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"201":{"description":"Risorsa creata (calculated-values)","content":{"application/json":{"schema":{"description":"Risorsa creata (calculated-values)","type":"object","additionalProperties":true,"example":{"id":1,"programId":1,"code":"LTV","name":"Lifetime value","resultType":"NUMBER","lastComputedAt":"2026-01-15T10:00:00.000Z","createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/calculated-values/{id}":{"get":{"tags":["Calculated Values"],"description":"Dettaglio di una programma fedeltà per identificativo del programma indicato in `programId`.\n\n**Contesto:** I **Valori calcolati** sono metriche derivate per cliente (es. LTV) ricalcolate periodicamente da regole di business.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n- `id` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Dettaglio calculated-values\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Calculated Values.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Dettaglio calculated-values","content":{"application/json":{"schema":{"description":"Dettaglio calculated-values","type":"object","additionalProperties":true,"example":{"id":1,"programId":1,"code":"LTV","name":"Lifetime value","resultType":"NUMBER","lastComputedAt":"2026-01-15T10:00:00.000Z","createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"put":{"tags":["Calculated Values"],"description":"Aggiorna una programma fedeltà (campi consentiti) del programma indicato in `programId`.\n\n**Contesto:** I **Valori calcolati** sono metriche derivate per cliente (es. LTV) ricalcolate periodicamente da regole di business.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n- `id` (integer) — obbligatorio\n\n**Body JSON:**\n- `name` (string) — opzionale\n- `description` (string) — opzionale\n- `dataSource` (object) — opzionale\n- `subjects` (string) — opzionale (valori: ANY_CUSTOMER, SAME_CUSTOMER)\n- `shopScope` (string) — opzionale (valori: ANY_SHOP, SAME_SHOP)\n- `where` (object) — opzionale\n- `time` (object) — opzionale\n- `status` (string) — opzionale (valori: ENABLED, DISABLED)\n\n**Risposte di successo:**\n- **200**: Aggiornamento calculated-values\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Calculated Values.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200},"description":{"anyOf":[{"type":"string"},{"type":"null"}]},"dataSource":{"type":"object","required":["type"],"properties":{"type":{"type":"string","enum":["total_spent","average_ticket","average_monthly_annualized","purchase_count","wallet_charge","wallet_discharge","used_vouchers","open_movements"]},"movementKind":{"type":"integer","minimum":1},"walletId":{"type":"integer","minimum":1},"applyMedianFloor":{"type":"boolean"},"medianFloorValue":{"type":"number","minimum":0}}},"subjects":{"type":"string","enum":["ANY_CUSTOMER","SAME_CUSTOMER"]},"shopScope":{"type":"string","enum":["ANY_SHOP","SAME_SHOP"]},"where":{"type":"object"},"time":{"type":"object"},"status":{"type":"string","enum":["ENABLED","DISABLED"]}}}}}},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Aggiornamento calculated-values","content":{"application/json":{"schema":{"description":"Aggiornamento calculated-values","type":"object","additionalProperties":true,"example":{"id":1,"programId":1,"code":"LTV","name":"Lifetime value","resultType":"NUMBER","lastComputedAt":"2026-01-15T10:00:00.000Z","createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"delete":{"tags":["Calculated Values"],"description":"Elimina una programma fedeltà del programma indicato in `programId` (soft delete o `TERMINATED` se vincoli FK).\n\n**Contesto:** I **Valori calcolati** sono metriche derivate per cliente (es. LTV) ricalcolate periodicamente da regole di business.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n- `id` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **204**: Operazione riuscita senza corpo di risposta.\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Calculated Values.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"204":{"description":"Operazione riuscita senza corpo di risposta."},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/calculated-values/{id}/recompute":{"post":{"tags":["Calculated Values"],"description":"Crea recompute del programma indicato in `programId`.\n\n**Contesto:** I **Valori calcolati** sono metriche derivate per cliente (es. LTV) ricalcolate periodicamente da regole di business.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n- `id` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Valore ricalcolato\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Calculated Values.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Valore ricalcolato","content":{"application/json":{"schema":{"description":"Valore ricalcolato","type":"object","additionalProperties":true,"example":{"id":1,"recomputed":true}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/calculated-values/recompute":{"post":{"tags":["Calculated Values"],"description":"Crea recompute del programma indicato in `programId`.\n\n**Contesto:** I **Valori calcolati** sono metriche derivate per cliente (es. LTV) ricalcolate periodicamente da regole di business.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Ricalcolo avviato\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Calculated Values.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Ricalcolo avviato","content":{"application/json":{"schema":{"description":"Ricalcolo avviato","type":"object","additionalProperties":true,"example":{"started":true}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/value-profile":{"get":{"tags":["ValueProfile"],"description":"Lista value-profile","security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"in":"path","name":"programId","required":true}],"responses":{"200":{"description":"Lista value-profile","content":{"application/json":{"schema":{"description":"Lista value-profile","type":"object","additionalProperties":true,"example":{"content":[{"id":1,"name":"Esempio","programId":1,"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}],"totalElements":1,"totalPages":1,"page":1,"size":20}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"put":{"tags":["ValueProfile"],"security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"in":"path","name":"programId","required":true}],"responses":{"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/value-profile/run":{"get":{"tags":["ValueProfile"],"security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"in":"path","name":"programId","required":true}],"responses":{"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/value-profile/recompute":{"post":{"tags":["ValueProfile"],"security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"in":"path","name":"programId","required":true}],"responses":{"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/customers/{id}/value-profile":{"get":{"tags":["ValueProfile"],"security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"in":"path","name":"id","required":true}],"responses":{"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/card-whitelists":{"get":{"tags":["CardWhitelists"],"description":"Elenco paginato di whitelist carte del programma indicato in `programId`.\n\n**Contesto:** Le **Whitelist carte** definiscono range o elenchi di numeri carta ammessi, con tier/wallet iniziale all'emissione.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n\n**Querystring:**\n- `page` (integer) — opzionale [default: 1]\n- `size` (integer) — opzionale [default: 20]\n\n**Risposte di successo:**\n- **200**: Lista card-whitelists\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** CardWhitelists.","parameters":[{"schema":{"type":"integer","minimum":1,"default":1},"in":"query","name":"page","required":false},{"schema":{"type":"integer","minimum":1,"maximum":500,"default":20},"in":"query","name":"size","required":false},{"schema":{"type":"integer"},"in":"path","name":"programId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Lista card-whitelists","content":{"application/json":{"schema":{"description":"Lista card-whitelists","type":"object","additionalProperties":true,"example":{"content":[{"id":1,"programId":1,"name":"Whitelist A","type":"RANGE","assignToNewCustomers":true,"rangeFrom":"000001","rangeTo":"999999","tierId":5,"tierName":"Gift Card","initialWalletId":2,"initialWalletName":"Gift Card","initialWalletSymbol":"GC","initialValue":25,"initialCurrencyId":1,"totalCards":999999,"activatedCards":1200,"availableCards":998799,"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}],"totalElements":1,"totalPages":1,"page":1,"size":20}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"post":{"tags":["CardWhitelists"],"description":"Create a card whitelist (LIST, RANGE, RANGE_CHECKDIGIT). Opzionale: `initialWalletId` + `initialValue` (unità wallet, come nel charge manuale) e `initialCurrencyId` per etichetta valuta; all’attivazione carta si accredita su quel wallet.\n\n**Contesto:** Le **Whitelist carte** definiscono range o elenchi di numeri carta ammessi, con tier/wallet iniziale all'emissione.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n\n**Body JSON:**\n- `name` (string) — obbligatorio\n- `description` (string) — opzionale\n- `type` (string) — obbligatorio (valori: LIST, RANGE, RANGE_CHECKDIGIT)\n- `assignToNewCustomers` (boolean) — opzionale\n- `isGiftCard` (boolean) — opzionale: Queste card sono Gift Cards fisiche (no auto-assegnazione nuovi clienti)\n- `tierId` (integer) — opzionale\n- `rangeFrom` (string) — opzionale\n- `rangeTo` (string) — opzionale\n- `entries` (array) — opzionale\n- `initialWalletId` (integer) — opzionale: Wallet del programma su cui precaricare initialValue\n- `initialValue` (number) — opzionale: Unità wallet da accreditare all’attivazione carta (es. 25 per €25 se 1 unità = 1€)\n- `initialCurrencyId` (integer) — opzionale: Valuta di riferimento (tabella currencies), opzionale\n\n**Risposte di successo:**\n- **201**: Risorsa creata (card-whitelists)\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** CardWhitelists.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["name","type"],"properties":{"name":{"type":"string","minLength":1},"description":{"type":"string"},"type":{"type":"string","enum":["LIST","RANGE","RANGE_CHECKDIGIT"]},"assignToNewCustomers":{"type":"boolean"},"isGiftCard":{"type":"boolean","description":"Queste card sono Gift Cards fisiche (no auto-assegnazione nuovi clienti)"},"tierId":{"type":"integer"},"rangeFrom":{"type":"string"},"rangeTo":{"type":"string"},"entries":{"type":"array","items":{"type":"string"}},"initialWalletId":{"type":"integer","description":"Wallet del programma su cui precaricare initialValue","nullable":true},"initialValue":{"type":"number","description":"Unità wallet da accreditare all’attivazione carta (es. 25 per €25 se 1 unità = 1€)","nullable":true},"initialCurrencyId":{"type":"integer","description":"Valuta di riferimento (tabella currencies), opzionale","nullable":true}}}}},"required":true},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"201":{"description":"Risorsa creata (card-whitelists)","content":{"application/json":{"schema":{"description":"Risorsa creata (card-whitelists)","type":"object","additionalProperties":true,"example":{"id":1,"programId":1,"name":"Whitelist A","type":"RANGE","assignToNewCustomers":true,"rangeFrom":"000001","rangeTo":"999999","tierId":5,"tierName":"Gift Card","initialWalletId":2,"initialWalletName":"Gift Card","initialWalletSymbol":"GC","initialValue":25,"initialCurrencyId":1,"totalCards":999999,"activatedCards":1200,"availableCards":998799,"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/card-whitelists/{id}":{"get":{"tags":["CardWhitelists"],"description":"Dettaglio whitelist. Include `totalCards`, `activatedCards`, `availableCards` (totale teorico, già assegnate a clienti, disponibili). LIST: voci in elenco; RANGE / RANGE_CHECKDIGIT: da range_from/range_to.\n\n**Contesto:** Le **Whitelist carte** definiscono range o elenchi di numeri carta ammessi, con tier/wallet iniziale all'emissione.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n- `id` (integer) — obbligatorio\n\n**Querystring:**\n- `page` (integer) — opzionale [default: 1]\n- `size` (integer) — opzionale [default: 100]\n\n**Risposte di successo:**\n- **200**: Dettaglio card-whitelists\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** CardWhitelists.","parameters":[{"schema":{"type":"integer","minimum":1,"default":1},"in":"query","name":"page","required":false},{"schema":{"type":"integer","minimum":1,"maximum":5000,"default":100},"in":"query","name":"size","required":false},{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Dettaglio card-whitelists","content":{"application/json":{"schema":{"description":"Dettaglio card-whitelists","type":"object","additionalProperties":true,"example":{"id":1,"programId":1,"name":"Whitelist A","type":"RANGE","assignToNewCustomers":true,"rangeFrom":"000001","rangeTo":"999999","tierId":5,"tierName":"Gift Card","initialWalletId":2,"initialWalletName":"Gift Card","initialWalletSymbol":"GC","initialValue":25,"initialCurrencyId":1,"totalCards":999999,"activatedCards":1200,"availableCards":998799,"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"put":{"tags":["CardWhitelists"],"description":"Aggiorna metadati whitelist (nome, tier, range, precarico wallet). Per voci LIST usare gli endpoint entries.\n\n**Contesto:** Le **Whitelist carte** definiscono range o elenchi di numeri carta ammessi, con tier/wallet iniziale all'emissione.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n- `id` (integer) — obbligatorio\n\n**Body JSON:**\n- `name` (string) — opzionale\n- `description` (string) — opzionale\n- `assignToNewCustomers` (boolean) — opzionale\n- `isGiftCard` (boolean) — opzionale\n- `tierId` (integer) — opzionale\n- `rangeFrom` (string) — opzionale\n- `rangeTo` (string) — opzionale\n- `initialWalletId` (integer) — opzionale\n- `initialValue` (number) — opzionale\n- `initialCurrencyId` (integer) — opzionale\n\n**Risposte di successo:**\n- **200**: Aggiornamento card-whitelists\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** CardWhitelists.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1},"description":{"type":"string"},"assignToNewCustomers":{"type":"boolean"},"isGiftCard":{"type":"boolean"},"tierId":{"type":"integer","nullable":true},"type":{"type":"string","enum":["LIST","RANGE","RANGE_CHECKDIGIT"],"description":"Solo passaggio tra RANGE e RANGE_CHECKDIGIT"},"rangeFrom":{"type":"string"},"rangeTo":{"type":"string"},"initialWalletId":{"type":"integer","nullable":true},"initialValue":{"type":"number","nullable":true},"initialCurrencyId":{"type":"integer","nullable":true}}}}}},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Aggiornamento card-whitelists","content":{"application/json":{"schema":{"description":"Aggiornamento card-whitelists","type":"object","additionalProperties":true,"example":{"id":1,"programId":1,"name":"Whitelist A","type":"RANGE","assignToNewCustomers":true,"rangeFrom":"000001","rangeTo":"999999","tierId":5,"tierName":"Gift Card","initialWalletId":2,"initialWalletName":"Gift Card","initialWalletSymbol":"GC","initialValue":25,"initialCurrencyId":1,"totalCards":999999,"activatedCards":1200,"availableCards":998799,"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"delete":{"tags":["CardWhitelists"],"description":"Elimina una programma fedeltà del programma indicato in `programId` (soft delete o `TERMINATED` se vincoli FK).\n\n**Contesto:** Le **Whitelist carte** definiscono range o elenchi di numeri carta ammessi, con tier/wallet iniziale all'emissione.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n- `id` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **204**: Operazione riuscita senza corpo di risposta.\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** CardWhitelists.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"204":{"description":"Operazione riuscita senza corpo di risposta."},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/card-whitelists/{id}/entries":{"post":{"tags":["CardWhitelists"],"description":"Crea entries del programma indicato in `programId`.\n\n**Contesto:** Le **Whitelist carte** definiscono range o elenchi di numeri carta ammessi, con tier/wallet iniziale all'emissione.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n- `id` (integer) — obbligatorio\n\n**Body JSON:**\n- `entries` (array) — obbligatorio\n\n**Risposte di successo:**\n- **201**: Voci aggiunte (LIST)\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** CardWhitelists.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["entries"],"properties":{"entries":{"type":"array","items":{"type":"string"},"minItems":1}}}}},"required":true},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"201":{"description":"Voci aggiunte (LIST)","content":{"application/json":{"schema":{"description":"Voci aggiunte (LIST)","type":"object","additionalProperties":true,"example":{"inserted":10,"whitelistId":1}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"delete":{"tags":["CardWhitelists"],"description":"Elimina una programma fedeltà del programma indicato in `programId` (soft delete o `TERMINATED` se vincoli FK).\n\n**Contesto:** Le **Whitelist carte** definiscono range o elenchi di numeri carta ammessi, con tier/wallet iniziale all'emissione.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n- `id` (integer) — obbligatorio\n\n**Body JSON:**\n- `entries` (array) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Voci rimosse (body: entries)\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** CardWhitelists.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["entries"],"properties":{"entries":{"type":"array","items":{"type":"string"},"minItems":1}}}}},"required":true},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Voci rimosse (body: entries)","content":{"application/json":{"schema":{"description":"Voci rimosse (body: entries)","type":"object","additionalProperties":true,"example":{"removed":3}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/card-whitelists/check":{"get":{"tags":["CardWhitelists"],"description":"Elenco paginato di check del programma indicato in `programId`.\n\n**Contesto:** Le **Whitelist carte** definiscono range o elenchi di numeri carta ammessi, con tier/wallet iniziale all'emissione.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n\n**Querystring:**\n- `cardNumber` (string) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Esito verifica + tier se ammessa; alreadyAssigned se carta già su customer_cards\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** CardWhitelists.","parameters":[{"schema":{"type":"string","minLength":1},"in":"query","name":"cardNumber","required":true},{"schema":{"type":"integer"},"in":"path","name":"programId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Esito verifica + tier se ammessa; alreadyAssigned se carta già su customer_cards","content":{"application/json":{"schema":{"description":"Esito verifica + tier se ammessa; alreadyAssigned se carta già su customer_cards","type":"object","additionalProperties":true,"example":{"allowed":true,"reason":"Card number not found in any whitelist of this program","whitelistId":1,"tierId":6,"tierName":"Gold","alreadyAssigned":false,"assignedToCustomerId":null}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/card-whitelists/check-batch":{"post":{"tags":["CardWhitelists"],"description":"Check multiple card numbers against program whitelists (for bulk import validation)","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["cardNumbers"],"properties":{"cardNumbers":{"type":"array","items":{"type":"string","minLength":1},"minItems":1,"maxItems":100000},"tierId":{"type":"integer"},"requireGiftCard":{"type":"boolean"}}}}},"required":true},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/card-whitelists/validate-overlap":{"post":{"tags":["CardWhitelists"],"description":"Crea validate-overlap del programma indicato in `programId`.\n\n**Contesto:** Le **Whitelist carte** definiscono range o elenchi di numeri carta ammessi, con tier/wallet iniziale all'emissione.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n\n**Body JSON:**\n- `type` (string) — obbligatorio (valori: LIST, RANGE, RANGE_CHECKDIGIT)\n- `rangeFrom` (string) — opzionale\n- `rangeTo` (string) — opzionale\n- `entries` (array) — opzionale\n\n**Risposte di successo:**\n- **200**: Sovrapposizione con whitelist esistenti (checkOverlap)\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** CardWhitelists.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["type"],"properties":{"type":{"type":"string","enum":["LIST","RANGE","RANGE_CHECKDIGIT"]},"rangeFrom":{"type":"string"},"rangeTo":{"type":"string"},"entries":{"type":"array","items":{"type":"string"}}}}}},"required":true},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Sovrapposizione con whitelist esistenti (checkOverlap)","content":{"application/json":{"schema":{"description":"Sovrapposizione con whitelist esistenti (checkOverlap)","type":"object","additionalProperties":true,"example":{"overlap":false,"overlappingWhitelists":[]}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/support-tickets":{"get":{"tags":["SupportTickets"],"description":"Elenco paginato di ticket supporto del programma indicato in `programId`.\n\n**Contesto:** I **Ticket supporto** permettono al cliente di aprire richieste assistenza tracciate nel backoffice.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n\n**Querystring:**\n- `status` (string) — opzionale (valori: Aperto, Chiuso, OPEN, CLOSED)\n- `ownerType` (string) — opzionale (valori: CUSTOMER, SHOP)\n- `page` (integer) — opzionale [default: 1]\n- `size` (integer) — opzionale [default: 20]\n\n**Risposte di successo:**\n- **200**: Ticket assistenza del programma\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** SupportTickets.","parameters":[{"schema":{"type":"string","enum":["Aperto","Chiuso","OPEN","CLOSED"]},"in":"query","name":"status","required":false},{"schema":{"type":"string","enum":["CUSTOMER","SHOP"]},"in":"query","name":"ownerType","required":false},{"schema":{"type":"integer","default":1},"in":"query","name":"page","required":false},{"schema":{"type":"integer","default":20},"in":"query","name":"size","required":false},{"schema":{"type":"integer"},"in":"path","name":"programId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Ticket assistenza del programma","content":{"application/json":{"schema":{"description":"Ticket assistenza del programma","type":"object","additionalProperties":true,"example":{"content":[{"id":1,"programId":1,"ownerType":"CUSTOMER","customerId":42,"shopOwnerId":null,"name":"Problema app","description":"Non riesco ad accedere","shopId":null,"createdAt":"2026-05-15T10:00:00.000Z","closedAt":null,"status":"Aperto","statusCode":"OPEN","foreignId":null,"kind":"Altro","details":[],"updatedAt":null}],"totalElements":1,"totalPages":1,"page":1,"size":20}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/customers/{id}/support-tickets":{"get":{"tags":["SupportTickets"],"description":"Elenco paginato di ticket supporto.\n\n**Contesto:** I **Ticket supporto** permettono al cliente di aprire richieste assistenza tracciate nel backoffice.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `id` (integer) — obbligatorio\n\n**Querystring:**\n- `status` (string) — opzionale (valori: Aperto, Chiuso, OPEN, CLOSED)\n- `page` (integer) — opzionale [default: 1]\n- `size` (integer) — opzionale [default: 20]\n\n**Risposte di successo:**\n- **200**: Ticket assistenza del cliente\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** SupportTickets.","parameters":[{"schema":{"type":"string","enum":["Aperto","Chiuso","OPEN","CLOSED"]},"in":"query","name":"status","required":false},{"schema":{"type":"integer","default":1},"in":"query","name":"page","required":false},{"schema":{"type":"integer","default":20},"in":"query","name":"size","required":false},{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Ticket assistenza del cliente","content":{"application/json":{"schema":{"description":"Ticket assistenza del cliente","type":"object","additionalProperties":true,"example":{"content":[{"id":1,"programId":1,"ownerType":"CUSTOMER","customerId":42,"shopOwnerId":null,"name":"Problema app","description":"Non riesco ad accedere","shopId":null,"createdAt":"2026-05-15T10:00:00.000Z","closedAt":null,"status":"Aperto","statusCode":"OPEN","foreignId":null,"kind":"Altro","details":[],"updatedAt":null}],"totalElements":1,"totalPages":1,"page":1,"size":20}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"post":{"tags":["SupportTickets"],"description":"Crea ticket supporto.\n\n**Contesto:** I **Ticket supporto** permettono al cliente di aprire richieste assistenza tracciate nel backoffice.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `id` (integer) — obbligatorio\n\n**Body JSON:**\n- `name` (string) — obbligatorio\n- `description` (string) — obbligatorio\n- `shopId` (integer) — opzionale\n- `foreignId` (string) — opzionale\n- `kind` (string) — opzionale\n- `details` (string) — opzionale\n\n**Risposte di successo:**\n- **201**: Crea ticket assistenza per un cliente.\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** SupportTickets.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["name","description"],"properties":{"name":{"type":"string","minLength":1},"description":{"type":"string","minLength":1},"shopId":{"type":"integer","nullable":true},"foreignId":{"type":"string","nullable":true},"kind":{"type":"string","nullable":true},"details":{"anyOf":[{"type":"array","items":{"type":"object","additionalProperties":true}},{"type":"string"},{"type":"null"}]}}}}},"required":true},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"201":{"description":"Crea ticket assistenza per un cliente.","content":{"application/json":{"schema":{"description":"Crea ticket assistenza per un cliente.","type":"object","additionalProperties":true,"example":{"id":1,"programId":1,"ownerType":"CUSTOMER","customerId":42,"shopOwnerId":null,"name":"Problema app","description":"Non riesco ad accedere","shopId":null,"createdAt":"2026-05-15T10:00:00.000Z","closedAt":null,"status":"Aperto","statusCode":"OPEN","foreignId":null,"kind":"Altro","details":[],"updatedAt":null}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/support-tickets/{ticketId}":{"get":{"tags":["SupportTickets"],"description":"Get one support ticket by id","parameters":[{"schema":{"type":"integer"},"in":"path","name":"ticketId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/support-tickets/{ticketId}/messages":{"get":{"tags":["SupportTickets"],"description":"Elenco paginato di messages.\n\n**Contesto:** I **Ticket supporto** permettono al cliente di aprire richieste assistenza tracciate nel backoffice.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `ticketId` (integer) — obbligatorio\n\n**Querystring:**\n- `page` (integer) — opzionale [default: 1]\n- `size` (integer) — opzionale [default: 20]\n\n**Risposte di successo:**\n- **200**: Messaggi di un ticket assistenza\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** SupportTickets.","parameters":[{"schema":{"type":"integer","default":1},"in":"query","name":"page","required":false},{"schema":{"type":"integer","default":20},"in":"query","name":"size","required":false},{"schema":{"type":"integer"},"in":"path","name":"ticketId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Messaggi di un ticket assistenza","content":{"application/json":{"schema":{"description":"Messaggi di un ticket assistenza","type":"object","additionalProperties":true,"example":{"content":[{"id":1,"ticketId":1,"message":"Buongiorno, come posso aiutarla?","viewed":false,"createdAt":"2026-05-15T10:00:00.000Z","createdBy":"support","fileUploaded":null}],"totalElements":1,"totalPages":1,"page":1,"size":20}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"post":{"tags":["SupportTickets"],"description":"Crea messages.\n\n**Contesto:** I **Ticket supporto** permettono al cliente di aprire richieste assistenza tracciate nel backoffice.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `ticketId` (integer) — obbligatorio\n\n**Body JSON:**\n- `message` (string) — obbligatorio\n- `fileUploaded` (string) — opzionale\n- `createdBy` (string) — opzionale (valori: customer, support)\n\n**Risposte di successo:**\n- **201**: Aggiunge messaggio a un ticket assistenza.\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** SupportTickets.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["message"],"properties":{"message":{"type":"string","minLength":1},"fileUploaded":{"type":"string","nullable":true},"createdBy":{"type":"string","enum":["customer","support"]}}}}},"required":true},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"ticketId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"201":{"description":"Aggiunge messaggio a un ticket assistenza.","content":{"application/json":{"schema":{"description":"Aggiunge messaggio a un ticket assistenza.","type":"object","additionalProperties":true,"example":{"id":1,"ticketId":1,"message":"Buongiorno, come posso aiutarla?","viewed":false,"createdAt":"2026-05-15T10:00:00.000Z","createdBy":"support","fileUploaded":null}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/support-tickets/{ticketId}/status":{"put":{"tags":["SupportTickets"],"description":"Aggiorna status (campi consentiti).\n\n**Contesto:** I **Ticket supporto** permettono al cliente di aprire richieste assistenza tracciate nel backoffice.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `ticketId` (integer) — obbligatorio\n\n**Body JSON:**\n- `status` (string) — obbligatorio (valori: Aperto, Chiuso, OPEN, CLOSED)\n\n**Risposte di successo:**\n- **200**: Aggiorna stato ticket assistenza.\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** SupportTickets.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["status"],"properties":{"status":{"type":"string","enum":["Aperto","Chiuso","OPEN","CLOSED"]},"details":{"anyOf":[{"type":"array","items":{"type":"object","additionalProperties":true}},{"type":"null"}]}}}}},"required":true},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"ticketId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Aggiorna stato ticket assistenza.","content":{"application/json":{"schema":{"description":"Aggiorna stato ticket assistenza.","type":"object","additionalProperties":true,"example":{"id":1,"programId":1,"ownerType":"CUSTOMER","customerId":42,"shopOwnerId":null,"name":"Problema app","description":"Non riesco ad accedere","shopId":null,"createdAt":"2026-05-15T10:00:00.000Z","closedAt":"2026-05-15T10:00:00.000Z","status":"Chiuso","statusCode":"CLOSED","foreignId":null,"kind":"Altro","details":[],"updatedAt":null}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/audit":{"get":{"tags":["Audit"],"description":"Elenco paginato di voce audit log.\n\n**Contesto:** L'**Audit log** registra azioni degli operatori su entità sensibili (chi, cosa, quando) per compliance e troubleshooting.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Querystring:**\n- `programId` (integer) — opzionale: Filtra per programma\n- `operatorId` (integer) — opzionale: Filtra per operatore (id)\n- `dateFrom` (string) — opzionale: Periodo da (incluso)\n- `dateTo` (string) — opzionale: Periodo al (incluso)\n- `functionality` (string) — opzionale: Filtra per funzionalità (es. Wallet, Tiers)\n- `functionalityOperation` (string) — opzionale: Filtra per operazione (es. Lettura, Creazione, Modifica, Eliminazione)\n- `object` (string) — opzionale: Cerca nel campo oggetto (LIKE)\n- `page` (integer) — opzionale [default: 1]\n- `size` (integer) — opzionale [default: 20]\n\n**Risposte di successo:**\n- **200**: Audit log\n\n**Errori documentati:** 400, 401, 403, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Audit.","parameters":[{"schema":{"type":"integer"},"in":"query","name":"programId","required":false,"description":"Filtra per programma"},{"schema":{"type":"integer"},"in":"query","name":"operatorId","required":false,"description":"Filtra per operatore (id)"},{"schema":{"type":"string","format":"date-time"},"in":"query","name":"dateFrom","required":false,"description":"Periodo da (incluso)"},{"schema":{"type":"string","format":"date-time"},"in":"query","name":"dateTo","required":false,"description":"Periodo al (incluso)"},{"schema":{"type":"string"},"in":"query","name":"functionality","required":false,"description":"Filtra per funzionalità (es. Wallet, Tiers)"},{"schema":{"type":"string"},"in":"query","name":"functionalityOperation","required":false,"description":"Filtra per operazione (es. Lettura, Creazione, Modifica, Eliminazione)"},{"schema":{"type":"string"},"in":"query","name":"object","required":false,"description":"Cerca nel campo oggetto (LIKE)"},{"schema":{"type":"integer","default":1},"in":"query","name":"page","required":false},{"schema":{"type":"integer","default":20},"in":"query","name":"size","required":false}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Audit log","content":{"application/json":{"schema":{"description":"Audit log","type":"object","additionalProperties":true,"example":{"content":[{"id":1,"action":"UPDATE","entityType":"customer","entityId":2,"operatorId":1,"createdAt":"2026-01-15T10:00:00.000Z"}],"totalElements":1,"totalPages":1,"page":1,"size":50}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/consents":{"get":{"tags":["Consents"],"description":"Elenco paginato di consenso del programma indicato in `programId`.\n\n**Contesto:** I **Consensi** tracciano privacy e opt-in del cliente (GDPR): definizioni configurabili, storico grant/revoke e verifica OTP.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n\n**Querystring:**\n- `page` (integer) — opzionale [default: 1]\n- `size` (integer) — opzionale [default: 50]\n- `includeDisabled` (boolean) — opzionale [default: false]\n\n**Risposte di successo:**\n- **200**: Lista consents\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Consents.","parameters":[{"schema":{"type":"integer","default":1},"in":"query","name":"page","required":false},{"schema":{"type":"integer","default":50},"in":"query","name":"size","required":false},{"schema":{"type":"boolean","default":false},"in":"query","name":"includeDisabled","required":false},{"schema":{"type":"integer"},"in":"path","name":"programId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Lista consents","content":{"application/json":{"schema":{"description":"Lista consents","type":"object","additionalProperties":true,"example":{"content":[{"id":1,"programId":1,"name":"Privacy","description":"Testo","required":false,"enabled":true,"expiryMonths":24,"sortOrder":1,"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}],"totalElements":1,"totalPages":1,"page":1,"size":20}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"post":{"tags":["Consents"],"description":"Crea consenso del programma indicato in `programId`.\n\n**Contesto:** I **Consensi** tracciano privacy e opt-in del cliente (GDPR): definizioni configurabili, storico grant/revoke e verifica OTP.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n\n**Body JSON:**\n- `name` (string) — obbligatorio\n- `description` (string) — opzionale\n- `required` (boolean) — opzionale [default: false]\n- `expiryMonths` (integer) — opzionale\n- `sortOrder` (integer) — opzionale [default: 0]\n- `enabled` (boolean) — opzionale [default: true]\n\n**Risposte di successo:**\n- **201**: Risorsa creata (consents)\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Consents.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["name"],"properties":{"name":{"type":"string"},"description":{"type":"string"},"required":{"type":"boolean","default":false},"expiryMonths":{"type":"integer"},"sortOrder":{"type":"integer","default":0},"enabled":{"type":"boolean","default":true},"defaultGranted":{"type":"boolean","default":false},"sendChannels":{"type":"array","items":{"type":"string"}}}}}},"required":true},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"201":{"description":"Risorsa creata (consents)","content":{"application/json":{"schema":{"description":"Risorsa creata (consents)","type":"object","additionalProperties":true,"example":{"id":1,"programId":1,"name":"Privacy","description":"Testo","required":false,"enabled":true,"expiryMonths":24,"sortOrder":1,"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/consents/{id}":{"get":{"tags":["Consents"],"description":"Dettaglio di una programma fedeltà per identificativo del programma indicato in `programId`.\n\n**Contesto:** I **Consensi** tracciano privacy e opt-in del cliente (GDPR): definizioni configurabili, storico grant/revoke e verifica OTP.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n- `id` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Dettaglio consents\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Consents.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Dettaglio consents","content":{"application/json":{"schema":{"description":"Dettaglio consents","type":"object","additionalProperties":true,"example":{"id":1,"programId":1,"name":"Privacy","description":"Testo","required":false,"enabled":true,"expiryMonths":24,"sortOrder":1,"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"put":{"tags":["Consents"],"description":"Aggiorna una programma fedeltà (campi consentiti) del programma indicato in `programId`.\n\n**Contesto:** I **Consensi** tracciano privacy e opt-in del cliente (GDPR): definizioni configurabili, storico grant/revoke e verifica OTP.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n- `id` (integer) — obbligatorio\n\n**Body JSON:**\n- `name` (string) — opzionale\n- `description` (string) — opzionale\n- `required` (boolean) — opzionale\n- `expiryMonths` (integer) — opzionale\n- `sortOrder` (integer) — opzionale\n- `enabled` (boolean) — opzionale\n\n**Risposte di successo:**\n- **200**: Aggiornamento consents\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Consents.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"description":{"type":"string"},"required":{"type":"boolean"},"expiryMonths":{"type":"integer","nullable":true},"sortOrder":{"type":"integer"},"enabled":{"type":"boolean"},"defaultGranted":{"type":"boolean"},"sendChannels":{"type":"array","items":{"type":"string"}}}}}}},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Aggiornamento consents","content":{"application/json":{"schema":{"description":"Aggiornamento consents","type":"object","additionalProperties":true,"example":{"id":1,"programId":1,"name":"Privacy","description":"Testo","required":false,"enabled":true,"expiryMonths":24,"sortOrder":1,"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"delete":{"tags":["Consents"],"description":"Elimina una programma fedeltà del programma indicato in `programId` (soft delete o `TERMINATED` se vincoli FK).\n\n**Contesto:** I **Consensi** tracciano privacy e opt-in del cliente (GDPR): definizioni configurabili, storico grant/revoke e verifica OTP.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `programId` (integer) — obbligatorio\n- `id` (integer) — obbligatorio\n\n**Risposte di successo:**\n- **204**: Operazione riuscita senza corpo di risposta.\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Consents.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"204":{"description":"Operazione riuscita senza corpo di risposta."},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/customers/{id}/consents":{"put":{"tags":["Consents"],"description":"Set customer consents. Array of { consentDefinitionId, granted }. Logs every change.\n\n**Contesto:** I **Consensi** tracciano privacy e opt-in del cliente (GDPR): definizioni configurabili, storico grant/revoke e verifica OTP.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `id` (integer) — obbligatorio\n\n**Body JSON:**\n- `consents` (array) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Consensi aggiornati\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Consents.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["consents"],"properties":{"consents":{"type":"array","items":{"type":"object","required":["consentDefinitionId","granted"],"properties":{"consentDefinitionId":{"type":"integer"},"granted":{"type":"boolean"}}}}}}}},"required":true},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Consensi aggiornati","content":{"application/json":{"schema":{"description":"Consensi aggiornati","type":"object","additionalProperties":true,"example":{"customerId":2,"updated":[{"consentDefinitionId":1,"granted":true}]}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/customers/{id}/consentsOTP":{"put":{"tags":["Consents"],"description":"Set customer consents with OTP verification. For POS flow: customer confirms via OTP sent to email/mobile.\n\n**Contesto:** I **Consensi** tracciano privacy e opt-in del cliente (GDPR): definizioni configurabili, storico grant/revoke e verifica OTP.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `id` (integer) — obbligatorio\n\n**Body JSON:**\n- `otp` (string) — obbligatorio\n- `consents` (array) — obbligatorio\n\n**Risposte di successo:**\n- **200**: Aggiorna consensi tramite OTP.\n\n**Errori documentati:** 400, 401, 403, 404, 409, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Consents.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["otp","consents"],"properties":{"otp":{"type":"string"},"consents":{"type":"array","items":{"type":"object","required":["consentDefinitionId","granted"],"properties":{"consentDefinitionId":{"type":"integer"},"granted":{"type":"boolean"}}}}}}}},"required":true},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Aggiorna consensi tramite OTP.","content":{"application/json":{"schema":{"description":"Aggiorna consensi tramite OTP.","type":"object","additionalProperties":true,"example":{"success":true}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/customers/{id}/consents/log":{"get":{"tags":["Consents"],"description":"Elenco paginato di log.\n\n**Contesto:** I **Consensi** tracciano privacy e opt-in del cliente (GDPR): definizioni configurabili, storico grant/revoke e verifica OTP.\n\n**Autenticazione:** richiede JWT Bearer (`Authorization: Bearer <token>`). Per operatori/terminali può essere richiesto anche `X-API-Key`.\n\n**Parametri path:**\n- `id` (integer) — obbligatorio\n\n**Querystring:**\n- `page` (integer) — opzionale [default: 1]\n- `size` (integer) — opzionale [default: 50]\n\n**Risposte di successo:**\n- **200**: Storico consensi\n\n**Errori documentati:** 400, 401, 403, 404, 500 (wrapper `ApiErrorEnvelope`, vedi schema condiviso).\n\n**Tag Swagger:** Consents.","parameters":[{"schema":{"type":"integer","default":1},"in":"query","name":"page","required":false},{"schema":{"type":"integer","default":50},"in":"query","name":"size","required":false},{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Storico consensi","content":{"application/json":{"schema":{"description":"Storico consensi","type":"object","additionalProperties":true,"example":{"content":[{"consentDefinitionId":1,"action":"GRANTED","at":"2026-01-15T10:00:00.000Z"}],"totalElements":1,"totalPages":1,"page":1,"size":20}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/tools/import-loyalty-customer":{"post":{"tags":["Tools"],"description":"Importa un cliente con card e saldo iniziale da migrazione loyalty legacy (una riga per chiamata). pointsWalletId è opzionale: richiesto solo se la riga ha pointsBalance > 0.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["tierId","row"],"properties":{"tierId":{"type":"integer"},"pointsWalletId":{"type":"integer","nullable":true},"creditsWalletId":{"type":"integer","nullable":true},"consentMappings":{"type":"object","properties":{"promotions":{"type":"integer","nullable":true},"statistics":{"type":"integer","nullable":true},"regolamento":{"type":"integer","nullable":true}}},"assignCardFromWhitelist":{"type":"boolean","default":false},"defaultRegistrationShopId":{"type":"integer","nullable":true},"allowMissingRegistrationShop":{"type":"boolean","default":false},"row":{"type":"object"}}}}},"required":true},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/tools/import-gift-card":{"post":{"tags":["Tools"],"description":"Importa una gift card fisica con cliente e saldo crediti regalo da migrazione legacy (una riga per chiamata).","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["tierId","row"],"properties":{"tierId":{"type":"integer"},"defaultRegistrationShopId":{"type":"integer","nullable":true},"row":{"type":"object"}}}}},"required":true},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/tools/repair-imported-gift-cards":{"post":{"tags":["Tools"],"description":"Ripara gift card importate con saldo su wallet legacy: emette gift_card_purchased e azzera il wallet.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["giftCreditsWalletId"],"properties":{"giftCreditsWalletId":{"type":"integer"},"defaultPurchaseShopId":{"type":"integer","nullable":true},"dryRun":{"type":"boolean","default":false},"limit":{"type":"integer","minimum":1,"maximum":5000,"default":500},"customerCardIds":{"type":"array","items":{"type":"integer"}}}}}},"required":true},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/tools/repair-imported-customer-balance-movements":{"post":{"tags":["Tools"],"description":"Riclassifica movimenti saldo iniziale importati (kind 100 + nota migrazione) in kind 102.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"dryRun":{"type":"boolean","default":false},"limit":{"type":"integer","minimum":1,"maximum":50000,"default":5000},"movementIds":{"type":"array","items":{"type":"integer"}},"customerIds":{"type":"array","items":{"type":"integer"}},"customerCardIds":{"type":"array","items":{"type":"integer"}}}}}}},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/tools/prepare-import-whitelist":{"post":{"tags":["Tools"],"description":"Crea una whitelist LIST per importazione con le card del CSV non già presenti in altre whitelist.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["cardNumbers","tierId"],"properties":{"cardNumbers":{"type":"array","items":{"type":"string","minLength":1},"minItems":1,"maxItems":100000},"tierId":{"type":"integer"},"isGiftCard":{"type":"boolean","default":false},"importKind":{"type":"string","enum":["loyalty-customers","gift-cards"]},"existingWhitelistId":{"type":"integer","nullable":true}}}}},"required":true},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/ai-connectors":{"get":{"tags":["AiConnectors"],"description":"Elenco connettori AI del programma (senza token in chiaro)","parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Lista ai-connectors","content":{"application/json":{"schema":{"description":"Lista ai-connectors","type":"object","additionalProperties":true,"example":{"content":[{"id":1,"name":"Esempio","programId":1,"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}],"totalElements":1,"totalPages":1,"page":1,"size":20}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"post":{"tags":["AiConnectors"],"description":"Crea connettore AI. Il token in chiaro è restituito una sola volta.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["name"],"properties":{"name":{"type":"string","minLength":1},"operatorId":{"type":"integer","description":"Default: operatore autenticato"},"scopes":{"type":"string","enum":["read","read_write"],"default":"read"},"expiresAt":{"type":"string","format":"date-time","nullable":true}}}}},"required":true},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"201":{"description":"Risorsa creata (ai-connectors)","content":{"application/json":{"schema":{"description":"Risorsa creata (ai-connectors)","type":"object","additionalProperties":true,"example":{"id":1,"name":"Esempio","programId":1,"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/ai-connectors/{id}":{"delete":{"tags":["AiConnectors"],"description":"Revoca (soft delete) un connettore AI","parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"204":{"description":"Operazione riuscita senza corpo di risposta."},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/ai-agents":{"get":{"tags":["AiAgents"],"description":"Catalogo skill e agenti del programma","parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Lista ai-agents","content":{"application/json":{"schema":{"description":"Lista ai-agents","type":"object","additionalProperties":true,"example":{"content":[{"id":1,"name":"Esempio","programId":1,"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}],"totalElements":1,"totalPages":1,"page":1,"size":20}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"post":{"tags":["AiAgents"],"description":"Accende un agente di catalogo, oppure crea un agente custom con regole proprie.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["skillKey"],"properties":{"skillKey":{"type":"string","enum":["antifraud","promotion","stock","batch","api","intrusion","custom"]},"name":{"type":"string"},"enabled":{"type":"boolean"},"autonomy":{"type":"string","enum":["osserva","segnala","agisce"]},"triggerType":{"type":"string","enum":["cron","event"]},"triggerConfig":{"type":"object"},"config":{"type":"object"}}}}},"required":true},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"201":{"description":"Risorsa creata (ai-agents)","content":{"application/json":{"schema":{"description":"Risorsa creata (ai-agents)","type":"object","additionalProperties":true,"example":{"id":1,"name":"Esempio","programId":1,"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/ai-token-usages":{"get":{"tags":["AiAgents"],"description":"Conteggio token del programma, per operatore e per sorgente.","parameters":[{"schema":{"type":"integer","minimum":1,"default":1},"in":"query","name":"page","required":false},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":50},"in":"query","name":"size","required":false},{"schema":{"type":"integer","minimum":1},"in":"query","name":"agentId","required":false},{"schema":{"type":"integer"},"in":"path","name":"programId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Lista ai-token-usages","content":{"application/json":{"schema":{"description":"Lista ai-token-usages","type":"object","additionalProperties":true,"example":{"content":[{"id":1,"name":"Esempio","programId":1,"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}],"totalElements":1,"totalPages":1,"page":1,"size":20}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/ai-agents/findings":{"get":{"tags":["AiAgents"],"description":"Finding degli agenti. status=open è l’inbox.","parameters":[{"schema":{"type":"string"},"in":"query","name":"status","required":false},{"schema":{"type":"integer"},"in":"query","name":"agentId","required":false},{"schema":{"type":"integer","default":50},"in":"query","name":"limit","required":false},{"schema":{"type":"integer"},"in":"path","name":"programId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/ai-agents/findings/{findingId}":{"put":{"tags":["AiAgents"],"description":"Accetta o scarta un finding. Accetta di una bozza antifrode crea la regola spenta.","requestBody":{"content":{"application/json":{"schema":{"type":"object","required":["status"],"properties":{"status":{"type":"string","enum":["accepted","dismissed","open"]}}}}},"required":true},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"findingId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/ai-agents/runs":{"get":{"tags":["AiAgents"],"parameters":[{"schema":{"type":"integer"},"in":"query","name":"agentId","required":false},{"schema":{"type":"integer","default":20},"in":"query","name":"limit","required":false},{"schema":{"type":"integer"},"in":"path","name":"programId","required":true}],"security":[{"bearerAuth":[]}],"responses":{"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/ai-agents/{id}":{"put":{"tags":["AiAgents"],"description":"Aggiornamento ai-agents","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"enabled":{"type":"boolean"},"autonomy":{"type":"string"},"triggerType":{"type":"string"},"triggerConfig":{"type":"object"},"config":{"type":"object"}}}}}},"parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Aggiornamento ai-agents","content":{"application/json":{"schema":{"description":"Aggiornamento ai-agents","type":"object","additionalProperties":true,"example":{"id":1,"name":"Esempio","programId":1,"createdAt":"2026-01-15T10:00:00.000Z","updatedAt":"2026-01-15T10:00:00.000Z"}}}}},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}},"delete":{"tags":["AiAgents"],"description":"Operazione riuscita senza corpo di risposta.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"204":{"description":"Operazione riuscita senza corpo di risposta."},"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/api/v1/programs/{programId}/ai-agents/{id}/run":{"post":{"tags":["AiAgents"],"description":"Esegue subito la skill. Non attiva promo e non blocca clienti.","parameters":[{"schema":{"type":"integer"},"in":"path","name":"programId","required":true},{"schema":{"type":"integer"},"in":"path","name":"id","required":true}],"security":[{"bearerAuth":[]}],"responses":{"400":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","content":{"application/json":{"schema":{"description":"Body, querystring o parametri non validi (validazione Fastify). Codice tipico: `FST_ERR_VALIDATION`. Anche la logica applicativa può rispondere 400 con codici propri della risorsa.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FST_ERR_VALIDATION","message":"body must have required property 'name'","language":"it","messages":{"it":"body must have required property 'name'","en":"body must have required property 'name'","es":"body must have required property 'name'"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"body must have required property 'name'"}}}}},"401":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","content":{"application/json":{"schema":{"description":"JWT assente, non valido o scaduto. Codici frequenti: `UNAUTHORIZED` (header Authorization mancante o malformato), `INVALID_TOKEN`, `TOKEN_EXPIRED`. Con API key: `API_KEY_INVALID`, ecc.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"UNAUTHORIZED","message":"Missing or invalid Authorization header. Use: Bearer <token>","language":"it","messages":{"it":"Missing or invalid Authorization header. Use: Bearer <token>","en":"Missing or invalid Authorization header. Use: Bearer <token>","es":"Missing or invalid Authorization header. Use: Bearer <token>"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing or invalid Authorization header. Use: Bearer <token>"}}}}},"403":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","content":{"application/json":{"schema":{"description":"Permessi insufficienti o contesto non consentito. Codici frequenti: `FORBIDDEN`, `MUST_CHANGE_PASSWORD` (l’operatore deve cambiare password prima di usare il resto dell’API), `FORBIDDEN_CUSTOMER`, `FORBIDDEN_POS`.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"FORBIDDEN","message":"Missing permission: PROGRAMS_READ","language":"it","messages":{"it":"Missing permission: PROGRAMS_READ","en":"Missing permission: PROGRAMS_READ","es":"Missing permission: PROGRAMS_READ"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Missing permission: PROGRAMS_READ"}}}}},"404":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","content":{"application/json":{"schema":{"description":"Risorsa non trovata. Il campo `error.code` dipende dall’endpoint (es. `PROGRAM_NOT_FOUND`, `CUSTOMER_NOT_FOUND`, `SHOP_NOT_FOUND`, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"PROGRAM_NOT_FOUND","message":"Program not found","language":"it","messages":{"it":"Program not found","en":"Program not found","es":"Program not found"},"path":"/api/v1/programs/999","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Program not found"}}}}},"409":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","content":{"application/json":{"schema":{"description":"Conflitto di stato o duplicato. Codici specifici per risorsa (es. `CARD_ALREADY_EXISTS`, email/username duplicato, …).","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"CARD_ALREADY_EXISTS","message":"Card already exists","language":"it","messages":{"it":"Card already exists","en":"Card already exists","es":"Card already exists"},"path":"/api/v1/customers/1/cards","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Card already exists"}}}}},"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/llms.txt":{"get":{"tags":["Agent"],"description":"Indice llms.txt dell'agente Marta (pubblico)","responses":{"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/ai/marta-agent.zip":{"get":{"tags":["Agent"],"description":"Pacchetto zip della skill Claude (pubblico, senza segreti)","responses":{"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/ai/openapi-agent.json":{"get":{"tags":["Agent"],"description":"OpenAPI filtrato (JSON) per ChatGPT Actions","responses":{"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/ai/{*}":{"get":{"tags":["Agent"],"description":"File pubblici della skill (SKILL.md, GPT.md, references, OpenAPI)","parameters":[{"schema":{"type":"string"},"in":"path","name":"*","required":true}],"responses":{"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}},"/mcp":{"post":{"tags":["Agent"],"description":"MCP Streamable HTTP (JSON-RPC). Auth: Bearer marta_conn_*.","responses":{"500":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","content":{"application/json":{"schema":{"description":"Errore interno. Nel gestore globale il codice predefinito è spesso `INTERNAL_ERROR` o quello dell’eccezione sollevata.","type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string","description":"Messaggio nella lingua del token (o Accept-Language). Sotto, se applicabile, IT/EN/ES."},"language":{"type":"string","enum":["it","en","es"]},"messages":{"type":"object","properties":{"it":{"type":"string"},"en":{"type":"string"},"es":{"type":"string"}}},"path":{"type":"string"},"timestamp":{"type":"string","format":"date-time"}},"required":["code","message"],"additionalProperties":true},"errordescrition":{"type":"string","description":"Alias legacy di error.message (POS ChangePassword / client 1.0)."}},"required":["error"],"additionalProperties":true,"example":{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","language":"it","messages":{"it":"Internal server error","en":"Internal server error","es":"Internal server error"},"path":"/api/v1/programs","timestamp":"2026-04-03T12:00:00.000Z"},"errordescrition":"Internal server error"}}}}}}}}},"servers":[{"url":"https://v2apistg.marta-platform.com","description":"MARTA Platform 2.0 API"}],"tags":[{"name":"Health","description":"Health check endpoints"},{"name":"Auth","description":"Authentication and authorization"},{"name":"Programs","description":"Programs management (SYSTEM)"},{"name":"Nets","description":"Networks management"},{"name":"Shops","description":"Shops management"},{"name":"Terminals","description":"Terminals management"},{"name":"Users","description":"Users/Operators management"},{"name":"Roles","description":"Roles management"},{"name":"Permissions","description":"Permissions listing"},{"name":"Wallets","description":"Wallets management"},{"name":"Tiers","description":"Tiers/Categories management"},{"name":"News","description":"News management"},{"name":"Cms","description":"Contenuti statici app (Strapi, tipi nuovi per programId)"},{"name":"Audiences","description":"Audiences management"},{"name":"Promotions","description":"Promotions management"},{"name":"ShopCategories","description":"Shop categories"},{"name":"CustomersFields","description":"Dynamic fields for customers"},{"name":"ProductCategories","description":"Product categories"},{"name":"Products","description":"Products management"},{"name":"PurchaseRules","description":"Purchase rules"},{"name":"PrizesCatalogs","description":"Prize catalogs"},{"name":"PrizesCategories","description":"Prize categories"},{"name":"Prizes","description":"Prizes management"},{"name":"PrizeRequests","description":"Prize request lifecycle (backoffice and customer)"},{"name":"PrizeWishlist","description":"Customer prize wishlist"},{"name":"PrizeEngagement","description":"Prize views, interactions, suggestions"},{"name":"BadgesCategories","description":"Badge categories"},{"name":"Badges","description":"Badges management"},{"name":"Tags","description":"Tags management"},{"name":"OpenMovements","description":"Open movements"},{"name":"Customers","description":"Customers management"},{"name":"Webhooks","description":"Webhooks management"},{"name":"Vouchers","description":"Vouchers management"},{"name":"Communications","description":"Communications (email/SMS/WhatsApp)"},{"name":"Movements","description":"Movements/Transactions"},{"name":"POS","description":"Point of Sale (terminal) operations"},{"name":"Donations","description":"Donations"},{"name":"DonationOngs","description":"Donation organizations"},{"name":"GiftCards","description":"Gift card catalog, variants, purchases and Stripe checkout"},{"name":"CouponOffers","description":"Coupon offers and Stripe purchases"},{"name":"Stripe","description":"Unified Stripe payment history (prizes, coupons, gift cards)"},{"name":"Reports","description":"Reports and analytics"},{"name":"AiConnectors","description":"AI connectors (MCP / ChatGPT Actions tokens)"},{"name":"Agent","description":"Public agent files and MCP endpoint"},{"name":"Antifraud","description":"Security engine anti-fraud rules, evaluation, investigations, and blocks"},{"name":"Common","description":"Common utilities (dictionary, currencies, timezones)"},{"name":"Countries","description":"Countries and geographic data"},{"name":"System","description":"System administration (API Keys, etc.)"}]}