{
    "openapi": "3.0.0",
    "info": {
        "title": "QRCODENOW API",
        "description": "API REST per creare e gestire i QR code del tuo account. Guida con esempi: /sviluppatori",
        "version": "1.0"
    },
    "paths": {
        "/api/v1/qrcodes": {
            "get": {
                "tags": [
                    "QrCodes"
                ],
                "summary": "Lista dei QR visibili all'utente (paginata)",
                "operationId": "66d2ffb4f12f04d5df49a0e5872b3c81",
                "parameters": [
                    {
                        "name": "p",
                        "in": "query",
                        "description": "Pagina, da 1",
                        "required": false,
                        "schema": {
                            "type": "integer",
                            "default": 1
                        }
                    },
                    {
                        "name": "c",
                        "in": "query",
                        "description": "Elementi per pagina (massimo 100)",
                        "required": false,
                        "schema": {
                            "type": "integer",
                            "default": 20
                        }
                    },
                    {
                        "name": "srt",
                        "in": "query",
                        "description": "Campo di ordinamento",
                        "required": false,
                        "schema": {
                            "type": "string",
                            "default": "date_create",
                            "enum": [
                                "date_create",
                                "date_update",
                                "title",
                                "scan_count"
                            ]
                        }
                    },
                    {
                        "name": "o",
                        "in": "query",
                        "description": "Verso dell'ordinamento",
                        "required": false,
                        "schema": {
                            "type": "string",
                            "default": "DESC",
                            "enum": [
                                "ASC",
                                "DESC"
                            ]
                        }
                    },
                    {
                        "name": "s",
                        "in": "query",
                        "description": "Ricerca su titolo, codice e slug (almeno 2 caratteri)",
                        "required": false,
                        "schema": {
                            "type": "string"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Lista paginata: rows (con my_role), total, count, page"
                    },
                    "401": {
                        "description": "API key mancante o non valida"
                    },
                    "429": {
                        "description": "Limite di 60 richieste al minuto superato (Retry-After: 60)"
                    }
                },
                "security": [
                    {
                        "apiKeyAuth": []
                    }
                ]
            },
            "post": {
                "tags": [
                    "QrCodes"
                ],
                "summary": "Crea un QR (chi crea diventa owner)",
                "operationId": "298e2cfa1b10de5b6f6e7edb819639dc",
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/IQrCodeCreate"
                            }
                        }
                    }
                },
                "responses": {
                    "201": {
                        "description": "QR creato: data contiene il QR con code e my_role"
                    },
                    "403": {
                        "description": "Quota QR del piano esaurita (code: plan_qr_limit)"
                    },
                    "422": {
                        "description": "Contenuto rifiutato: campo mancante, URL non valido, tipo non supportato"
                    }
                },
                "security": [
                    {
                        "apiKeyAuth": []
                    }
                ]
            }
        },
        "/api/v1/qrcodes/{id}": {
            "get": {
                "tags": [
                    "QrCodes"
                ],
                "summary": "Dettaglio di un QR",
                "operationId": "56946587600ec16850eb3aa2d0ab4c9e",
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "description": "Id numerico del QR",
                        "required": true,
                        "schema": {
                            "type": "integer"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Il QR richiesto, con my_role"
                    },
                    "403": {
                        "description": "Il QR esiste ma non e' condiviso con l'utente"
                    },
                    "404": {
                        "description": "QR inesistente o eliminato"
                    }
                },
                "security": [
                    {
                        "apiKeyAuth": []
                    }
                ]
            },
            "delete": {
                "tags": [
                    "QrCodes"
                ],
                "summary": "Elimina un QR (soft-delete). Solo owner.",
                "operationId": "cb6cbf6ee9ec4cb388825c0ad9a33fd7",
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "description": "Id numerico del QR",
                        "required": true,
                        "schema": {
                            "type": "integer"
                        }
                    }
                ],
                "responses": {
                    "204": {
                        "description": "Eliminato"
                    },
                    "403": {
                        "description": "Solo l'owner puo' eliminare il QR"
                    }
                },
                "security": [
                    {
                        "apiKeyAuth": []
                    }
                ]
            },
            "patch": {
                "tags": [
                    "QrCodes"
                ],
                "summary": "Aggiorna titolo o destinazione di un QR",
                "operationId": "ce9781cb84de1b82401e0f6cbabe4f5e",
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "description": "Id numerico del QR",
                        "required": true,
                        "schema": {
                            "type": "integer"
                        }
                    }
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/IQrCodePatch"
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Il QR aggiornato"
                    },
                    "403": {
                        "description": "Serve almeno il ruolo editor sul QR"
                    }
                },
                "security": [
                    {
                        "apiKeyAuth": []
                    }
                ]
            }
        },
        "/api/v1/qrcodes/static": {
            "post": {
                "tags": [
                    "QrCodes"
                ],
                "summary": "Genera un QR statico da testo senza salvarlo",
                "operationId": "5618e2e4dc1254646a389394d06d86a3",
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/IStaticQrCode"
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Immagine PNG del QR statico",
                        "content": {
                            "image/png": {}
                        }
                    },
                    "400": {
                        "description": "Body JSON mancante o non valido"
                    },
                    "401": {
                        "description": "API key mancante o non valida; i JWT non sono accettati"
                    },
                    "422": {
                        "description": "Testo vuoto o non rappresentabile"
                    }
                },
                "security": [
                    {
                        "apiKeyAuth": []
                    }
                ]
            }
        },
        "/api/v1/qrcodes/{id}/members": {
            "get": {
                "tags": [
                    "QrMembers"
                ],
                "summary": "Membri di un QR",
                "operationId": "6a2ed2e5043924cce337c296d547a3ea",
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "description": "Id numerico del QR",
                        "required": true,
                        "schema": {
                            "type": "integer"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Elenco membri (inclusi i pendenti) + my_role"
                    }
                },
                "security": [
                    {
                        "apiKeyAuth": []
                    }
                ]
            },
            "post": {
                "tags": [
                    "QrMembers"
                ],
                "summary": "Aggiunge un membro: solo il proprietario o un membro dello spazio del QR",
                "operationId": "d829e9003527fd5e1b8a2f424e719eb6",
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "description": "Id numerico del QR",
                        "required": true,
                        "schema": {
                            "type": "integer"
                        }
                    }
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/IQrMemberCreate"
                            }
                        }
                    }
                },
                "responses": {
                    "201": {
                        "description": "Membro aggiunto"
                    },
                    "409": {
                        "description": "Utente gia' membro di questo QR"
                    },
                    "422": {
                        "description": "Ruolo non assegnabile, oppure utente inesistente o fuori dallo spazio del QR (code: invalid_user)"
                    }
                },
                "security": [
                    {
                        "apiKeyAuth": []
                    }
                ]
            }
        },
        "/api/v1/qrcodes/{id}/members/{iduser}": {
            "put": {
                "tags": [
                    "QrMembers"
                ],
                "summary": "Cambia il ruolo di un membro",
                "operationId": "11b2c95fcc115f49d9a0889dee072ef0",
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "description": "Id numerico del QR",
                        "required": true,
                        "schema": {
                            "type": "integer"
                        }
                    },
                    {
                        "name": "iduser",
                        "in": "path",
                        "description": "Id dell'utente membro",
                        "required": true,
                        "schema": {
                            "type": "integer"
                        }
                    }
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/IQrMemberRoleUpdate"
                            }
                        }
                    }
                },
                "responses": {
                    "200": {
                        "description": "Ruolo aggiornato"
                    },
                    "409": {
                        "description": "Ultimo owner: non declassabile"
                    }
                },
                "security": [
                    {
                        "apiKeyAuth": []
                    }
                ]
            },
            "delete": {
                "tags": [
                    "QrMembers"
                ],
                "summary": "Rimuove un membro",
                "operationId": "874302af773581fcb4945aec1192346a",
                "parameters": [
                    {
                        "name": "id",
                        "in": "path",
                        "description": "Id numerico del QR",
                        "required": true,
                        "schema": {
                            "type": "integer"
                        }
                    },
                    {
                        "name": "iduser",
                        "in": "path",
                        "description": "Id dell'utente membro",
                        "required": true,
                        "schema": {
                            "type": "integer"
                        }
                    }
                ],
                "responses": {
                    "204": {
                        "description": "Membro rimosso"
                    }
                },
                "security": [
                    {
                        "apiKeyAuth": []
                    }
                ]
            }
        },
        "/i/{code}.png": {
            "get": {
                "tags": [
                    "Immagini"
                ],
                "summary": "PNG del QR code (rigenerato al volo)",
                "operationId": "c182c652f9331e155ea862c5a19ffde4",
                "parameters": [
                    {
                        "name": "code",
                        "in": "path",
                        "description": "Codice pubblico del QR (campo code)",
                        "required": true,
                        "schema": {
                            "type": "string"
                        }
                    },
                    {
                        "name": "download",
                        "in": "query",
                        "description": "Se valorizzato, forza il salvataggio invece dell'anteprima",
                        "required": false,
                        "schema": {
                            "type": "integer"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Immagine PNG del QR"
                    },
                    "404": {
                        "description": "Codice inesistente o QR disattivato"
                    }
                }
            }
        },
        "/i/{code}.svg": {
            "get": {
                "tags": [
                    "Immagini"
                ],
                "summary": "SVG vettoriale del QR code, per la stampa (stesso stile del PNG)",
                "operationId": "92baa798af9f0555dfd80f40e33add31",
                "parameters": [
                    {
                        "name": "code",
                        "in": "path",
                        "description": "Codice pubblico del QR (campo code)",
                        "required": true,
                        "schema": {
                            "type": "string"
                        }
                    },
                    {
                        "name": "download",
                        "in": "query",
                        "description": "Se valorizzato, forza il salvataggio invece dell'anteprima",
                        "required": false,
                        "schema": {
                            "type": "integer"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Immagine SVG del QR"
                    },
                    "404": {
                        "description": "Codice inesistente o QR disattivato"
                    }
                }
            }
        }
    },
    "components": {
        "schemas": {
            "IQrCodeCreate": {
                "required": [
                    "idtype"
                ],
                "properties": {
                    "idtype": {
                        "description": "Tipo di QR (FK qr_types)",
                        "type": "integer"
                    },
                    "title": {
                        "type": "string",
                        "nullable": true
                    },
                    "target_url": {
                        "description": "URL di destinazione del QR dinamico (obbligatorio per i tipi 1, 2, 3, 5)",
                        "type": "string",
                        "nullable": true
                    },
                    "qr_payload": {
                        "description": "Contenuto tipizzato per tipo (vCard, WiFi, ...) — pass-through verso il motore",
                        "type": "object"
                    }
                },
                "type": "object"
            },
            "IQrCodePatch": {
                "properties": {
                    "title": {
                        "type": "string",
                        "nullable": true
                    },
                    "target_url": {
                        "type": "string",
                        "nullable": true
                    }
                },
                "type": "object"
            },
            "IStaticQrCode": {
                "required": [
                    "text"
                ],
                "properties": {
                    "text": {
                        "description": "Testo codificato direttamente nel QR; il limite e in byte UTF-8",
                        "type": "string",
                        "example": "Testo da codificare",
                        "maxLength": 1273,
                        "minLength": 1
                    }
                },
                "type": "object"
            },
            "IQrMemberCreate": {
                "description": "Body di POST /api/v1/qrcodes/{id}/members.\n\n`owner` NON e' fra i valori ammessi: si diventa owner creando il QR o con un\ntrasferimento esplicito di proprieta', non con un invito (v. QrRole::invitable()).",
                "properties": {
                    "iduser": {
                        "type": "integer"
                    },
                    "role": {
                        "type": "string"
                    }
                },
                "type": "object"
            },
            "IQrMemberRoleUpdate": {
                "description": "Body di PUT /api/v1/qrcodes/{id}/members/{iduser}.\n\nQui `owner` E' ammesso: promuovere un membro a owner e' il trasferimento di\nproprieta', consentito al solo owner (guardia in\nQrMembershipRepository::changeMemberRole).",
                "properties": {
                    "role": {
                        "type": "string"
                    }
                },
                "type": "object"
            }
        },
        "securitySchemes": {
            "apiKeyAuth": {
                "type": "http",
                "description": "Chiave API creata in Impostazioni → API e webhook. I token di sessione (JWT) non sono accettati.",
                "name": "Authorization",
                "in": "header",
                "bearerFormat": "ogqr_<64 caratteri esadecimali>",
                "scheme": "bearer"
            }
        }
    },
    "tags": [
        {
            "name": "QrCodes",
            "description": "QrCodes"
        },
        {
            "name": "QrMembers",
            "description": "QrMembers"
        },
        {
            "name": "Immagini",
            "description": "Immagini"
        }
    ]
}