{"openapi":"3.0.0","info":{"title":"MARTA Platform 2.0 API — Customer","description":"REST API per il token cliente (JWT `operatorType=CUSTOMER`).\n\nOperazioni che quel token può chiamare. Se il path contiene l'id del cliente, deve essere il `sub` del token; se contiene `programId`, deve coincidere con `programId` del token. `PUT /api/v1/addresses/{id}` è consentita solo sull'indirizzo del cliente.\n\nIl token si ottiene con `POST /oauth/customer` e si rinnova con `POST /oauth/refresh`. In questa vista ci sono anche le route pubbliche dello stesso client (registrazione, OTP, reset password, attivazione B2B): non richiedono il Bearer del cliente.\n\nIl catalogo completo degli operatori è su [/docs](/docs).","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":{"/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/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/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"}}}}}}}},"/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"}}}}}}}},"/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"}}}}}}}},"/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/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/addresses":{"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}":{"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"}}}}}}}},"/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/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/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}/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/{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}/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/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/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/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"}}}}}}}},"/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"}}}}}}}},"/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"}}}}}}}},"/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"}}}}}}}},"/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"}}}}}}}},"/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"}}}}}}}},"/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"}}}}}}}},"/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"}}}}}}}},"/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"}}}}}}}},"/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"}}}}}}}},"/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"}}}}}}}},"/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"}}}}}}}},"/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"}}}}}}}},"/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"}}}}}}}},"/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"}}}}}}}},"/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"}}}}}}}},"/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"}}}}}}}},"/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"}}}}}}}},"/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"}}}}}}}},"/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"}}}}}}}},"/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"}}}}}}}},"/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"}}}}}}}},"/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"}}}}}}}},"/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"}}}}}}}},"/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/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-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}/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/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-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/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/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":{"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"}}}}}}}},"/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"}}}}}}}},"/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"}}}}}}}},"/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"}}}}}}}},"/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/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/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"}}}}}}}},"/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"}}}}}}}},"/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}/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"}}}}}}}},"/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"}}}}}}}},"/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"}}}}}}}},"/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"}}}}}}}},"/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/communication-messages/{id}":{"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/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"}}}}}}}},"/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/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}/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/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"}}}}}}}},"/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}/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"}}}}}}}}},"servers":[{"url":"https://v2apistg.marta-platform.com","description":"MARTA Platform 2.0 API"}],"tags":[{"name":"Auth","description":"Authentication and authorization"},{"name":"Programs","description":"Programs management (SYSTEM)"},{"name":"Shops","description":"Shops management"},{"name":"Users","description":"Users/Operators management"},{"name":"Wallets","description":"Wallets management"},{"name":"Tiers","description":"Tiers/Categories management"},{"name":"News","description":"News management"},{"name":"Promotions","description":"Promotions management"},{"name":"ShopCategories","description":"Shop categories"},{"name":"CustomersFields","description":"Dynamic fields for customers"},{"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":"Customers","description":"Customers management"},{"name":"Vouchers","description":"Vouchers management"},{"name":"Communications","description":"Communications (email/SMS/WhatsApp)"},{"name":"Movements","description":"Movements/Transactions"},{"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"}]}