{
  "openapi": "3.0.0",
  "info": {
    "title": "Authentification",
    "description": "Authentification commune à toutes les APIs de la plateforme (Hubdoc,\nLoctavia, Octopia). Un seul serveur OAuth2 (Doorkeeper) délivre les\ntokens ; chaque produit consomme ensuite le même `Bearer`.\n\n## Obtenir un token\n\n| Cas d'usage | Flow | Endpoints |\n|-------------|------|-----------|\n| Application frontend / tierce | Authorization code | `GET /oauth/authorize` puis `POST /oauth/token` |\n| Application de confiance (mobile, tests) | Password | `POST /oauth/token` |\n| CLI `hubdoc` (sans navigateur) | Device flow (RFC 8628 simplifié) | `POST /api/v1/cli/auth/device` puis polling |\n| SSO Edifice (JWT) | Token exchange | `POST /api/v1/oauth/token_exchange` |\n| Intégrations serveur | Personal Access Token (PAT) | généré depuis l'interface ou via le device flow CLI |\n\n## Scopes\n\nLes scopes sont appliqués par verbe HTTP sur toutes les APIs :\n\n- `read` : requêtes `GET` uniquement (scope par défaut des PAT)\n- `write` : inclut les mutations (`POST`, `PATCH`, `PUT`, `DELETE`)\n- `admin` : accès administratif\n\nUn token sans le scope requis reçoit un `403` enveloppé ; un token\nexpiré ou révoqué reçoit un `401`.\n\n## Cas particuliers\n\n- **Loctavia** : en plus du `Bearer`, chaque requête (hors\n  `GET /loctavia/api/v1/mandataires`) exige l'en-tête `X-Mandataire-Id`\n  (tenant). Voir la [doc Loctavia](../loctavia/).\n- **Octopia** : les connecteurs d'ingestion s'authentifient par clé API\n  (`X-Connector-Key`), sans OAuth2. Voir la [doc Octopia](../octopia/).\n",
    "version": "1.0.0",
    "contact": {
      "name": "API Support",
      "email": "support@sinoia.fr"
    }
  },
  "servers": [
    {
      "url": "http://localhost:3000",
      "description": "Serveur de développement"
    },
    {
      "url": "https://rec.hubdoc.sinoia.cloud",
      "description": "Serveur de recette"
    }
  ],
  "security": [],
  "tags": [
    {
      "name": "Authentication",
      "description": "OAuth2 authentication endpoints"
    },
    {
      "name": "CLI Auth",
      "description": "Endpoints du device flow CLI (RFC 8628). Permettent au binaire\n`hubdoc` (cf sinoia/hubdoc-tools) de s'authentifier sans\nmanipulation manuelle de tokens : `hubdoc login` ouvre le\nnavigateur, l'user confirme, le CLI ramasse son PAT\nautomatiquement.\n"
    }
  ],
  "paths": {
    "/oauth/token": {
      "post": {
        "summary": "Obtenir un token d'accès OAuth2",
        "description": "Obtenir un token d'accès OAuth2 en utilisant différents flows :\n\n- **password** : Pour applications de confiance (mobile, CLI, tests) - envoie email/mot de passe directement\n- **authorization_code** : Pour applications frontend/tierce - échange un code d'autorisation contre un token\n- **refresh_token** : Pour rafraîchir un token expiré\n",
        "tags": [
          "Authentication"
        ],
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "required": [
                  "grant_type",
                  "client_id",
                  "client_secret"
                ],
                "properties": {
                  "grant_type": {
                    "type": "string",
                    "enum": [
                      "password",
                      "authorization_code",
                      "refresh_token"
                    ],
                    "description": "Type d'autorisation OAuth2",
                    "example": "authorization_code"
                  },
                  "client_id": {
                    "type": "string",
                    "description": "Identifiant client OAuth2",
                    "example": "Q0OEEVE5kLDll1uAek5B-rJcXJCtkIoEdPwLvDiQ888"
                  },
                  "client_secret": {
                    "type": "string",
                    "description": "Secret client OAuth2",
                    "example": "jnZuQF8PFv42WmA5vLLBdPrhBAwAqLuBTERGMUERUPE"
                  },
                  "username": {
                    "type": "string",
                    "description": "Adresse email de l'utilisateur (requis pour grant_type=password)",
                    "example": "admin@example.com"
                  },
                  "password": {
                    "type": "string",
                    "description": "Mot de passe de l'utilisateur (requis pour grant_type=password)",
                    "example": "MyPassword123"
                  },
                  "code": {
                    "type": "string",
                    "description": "Code d'autorisation (requis pour grant_type=authorization_code)",
                    "example": "AUTH_CODE_FROM_AUTHORIZE_ENDPOINT"
                  },
                  "redirect_uri": {
                    "type": "string",
                    "format": "uri",
                    "description": "URI de redirection (requis pour grant_type=authorization_code)",
                    "example": "http://localhost:8082/callback"
                  },
                  "refresh_token": {
                    "type": "string",
                    "description": "Jeton de rafraîchissement (requis pour grant_type=refresh_token)",
                    "example": "def50200cde4321cfa9..."
                  },
                  "scope": {
                    "type": "string",
                    "description": "Portée des permissions demandées (optionnelle)",
                    "example": "read write"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Token d'accès généré avec succès",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TokenResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/oauth/authorize": {
      "get": {
        "summary": "Initier l'autorisation OAuth2 (flow authorization_code)",
        "description": "Redirige l'utilisateur vers l'interface d'autorisation OAuth2.\nL'utilisateur doit s'authentifier et autoriser l'application.\nAprès autorisation, l'utilisateur est redirigé vers l'URI spécifiée avec un code d'autorisation.\n",
        "tags": [
          "Authentication"
        ],
        "security": [],
        "parameters": [
          {
            "name": "response_type",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "code"
              ],
              "example": "code"
            },
            "description": "Type de réponse OAuth2 (toujours \"code\")"
          },
          {
            "name": "client_id",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "example": "Q0OEEVE5kLDll1uAek5B-rJcXJCtkIoEdPwLvDiQ888"
            },
            "description": "Identifiant client OAuth2"
          },
          {
            "name": "redirect_uri",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uri",
              "example": "http://localhost:3000/callback"
            },
            "description": "URI de redirection après autorisation"
          },
          {
            "name": "scope",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "example": "read write"
            },
            "description": "Portée des permissions demandées"
          },
          {
            "name": "state",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "example": "random_state_string"
            },
            "description": "Valeur aléatoire pour prévenir les attaques CSRF"
          }
        ],
        "responses": {
          "302": {
            "description": "Redirection vers l'interface d'autorisation ou vers l'URI de callback",
            "headers": {
              "Location": {
                "description": "URL de redirection",
                "schema": {
                  "type": "string",
                  "example": "http://localhost:3000/callback?code=AUTH_CODE&state=random_state_string"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/oauth/revoke": {
      "post": {
        "summary": "Révoquer un token OAuth2",
        "description": "Révoque un token d'accès ou un refresh token OAuth2.\nConforme à RFC 7009 (OAuth 2.0 Token Revocation).\n\nUne fois révoqué, le token ne peut plus être utilisé pour accéder aux ressources protégées.\n",
        "tags": [
          "Authentication"
        ],
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "required": [
                  "token",
                  "client_id",
                  "client_secret"
                ],
                "properties": {
                  "token": {
                    "type": "string",
                    "description": "Token à révoquer (access_token ou refresh_token)",
                    "example": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
                  },
                  "token_type_hint": {
                    "type": "string",
                    "enum": [
                      "access_token",
                      "refresh_token"
                    ],
                    "description": "Indice sur le type de token (optionnel, améliore les performances)",
                    "example": "access_token"
                  },
                  "client_id": {
                    "type": "string",
                    "description": "Identifiant client OAuth2",
                    "example": "Q0OEEVE5kLDll1uAek5B-rJcXJCtkIoEdPwLvDiQ888"
                  },
                  "client_secret": {
                    "type": "string",
                    "description": "Secret client OAuth2",
                    "example": "jnZuQF8PFv42WmA5vLLBdPrhBAwAqLuBTERGMUERUPE"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Token révoqué avec succès (ou token déjà invalide/inexistant)",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": {
                      "type": "string",
                      "example": "Token revoked successfully"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/api/v1/oauth/token_exchange": {
      "post": {
        "summary": "Échanger un JWT Edifice contre un token d'accès",
        "description": "Permet à Edifice d'authentifier ses utilisateurs dans Corex via un échange de token JWT.\n\n**Flow:**\n1. Edifice génère un JWT signé (RS256) contenant les informations utilisateur\n2. Corex vérifie la signature via JWKS (JSON Web Key Set)\n3. Si l'utilisateur n'existe pas, il est créé automatiquement\n4. Un token d'accès Doorkeeper est retourné\n\n**JWT Claims attendus:**\n- `sub` - Identifiant utilisateur Edifice\n- `email` - Email de l'utilisateur (requis)\n- `first_name` - Prénom\n- `last_name` - Nom\n- `iss` - Doit être \"edifice\"\n- `aud` - Doit être \"corex\"\n- `exp` - Timestamp d'expiration\n",
        "tags": [
          "Authentication"
        ],
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/TokenExchangeRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Token d'accès généré avec succès",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TokenExchangeResponse"
                },
                "example": {
                  "access_token": "abc123def456...",
                  "token_type": "Bearer",
                  "expires_in": 7200,
                  "created_at": 1705410000
                }
              }
            }
          },
          "400": {
            "description": "Requête invalide (grant_type non supporté ou assertion manquante)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OAuthError"
                },
                "examples": {
                  "unsupported_grant_type": {
                    "summary": "Grant type non supporté",
                    "value": {
                      "error": "unsupported_grant_type"
                    }
                  },
                  "missing_assertion": {
                    "summary": "Assertion manquante",
                    "value": {
                      "error": "invalid_request",
                      "error_description": "assertion required"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Authentification échouée (client invalide ou JWT invalide)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OAuthError"
                },
                "examples": {
                  "invalid_client": {
                    "summary": "Client OAuth invalide",
                    "value": {
                      "error": "invalid_client"
                    }
                  },
                  "invalid_grant": {
                    "summary": "JWT invalide",
                    "value": {
                      "error": "invalid_grant",
                      "error_description": "JWT verification failed: signature invalid"
                    }
                  }
                }
              }
            }
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/api/v1/cli/auth/device": {
      "post": {
        "security": [],
        "summary": "Démarrer un device flow CLI (RFC 8628)",
        "description": "Initie une session d'authentification \"device flow\" pour le CLI\nHubdoc. Endpoint public — pas d'auth Doorkeeper requise.\n\nLe flow complet :\n\n1. CLI appelle ce endpoint → reçoit `device_code` (secret) + `user_code`\n   (lisible humain, format `XXXX-XXXX`) + `verification_uri`.\n2. CLI affiche le user_code à l'utilisateur et ouvre la\n   `verification_uri_complete` dans son navigateur.\n3. L'user (déjà loggé sur Hubdoc) confirme le code sur la page\n   `/cli/activation`.\n4. CLI poll `/api/v1/cli/auth/device/poll` jusqu'à approval.\n5. À l'approval, le poll retourne un `access_token` (un Personal\n   Access Token utilisable comme Bearer sur toute l'API).\n\nRate-limited à 20 appels par 5 minutes (par IP).\n",
        "tags": [
          "CLI Auth"
        ],
        "responses": {
          "200": {
            "description": "Device flow démarré",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "device_code",
                    "user_code",
                    "verification_uri",
                    "expires_in",
                    "interval"
                  ],
                  "properties": {
                    "device_code": {
                      "type": "string",
                      "description": "Secret à présenter dans le polling. 256 bits d'entropie.",
                      "example": "8a3f2e7b9c1d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f"
                    },
                    "user_code": {
                      "type": "string",
                      "description": "Code lisible à présenter à l'utilisateur (8 chars, alphabet sans 0/O/1/I/L)",
                      "example": "WDJB-MJHT"
                    },
                    "verification_uri": {
                      "type": "string",
                      "description": "URL où l'utilisateur saisit le user_code",
                      "example": "https://hubdoc.example.com/cli/activation/new"
                    },
                    "verification_uri_complete": {
                      "type": "string",
                      "description": "URL pré-remplie avec le user_code en query string",
                      "example": "https://hubdoc.example.com/cli/activation/new?code=WDJB-MJHT"
                    },
                    "expires_in": {
                      "type": "integer",
                      "description": "Durée de validité du flow en secondes (10 min)",
                      "example": 600
                    },
                    "interval": {
                      "type": "integer",
                      "description": "Intervalle de polling recommandé en secondes",
                      "example": 5
                    }
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limit dépassé (20 / 5 min)"
          }
        }
      }
    },
    "/api/v1/cli/auth/device/poll": {
      "post": {
        "security": [],
        "summary": "Poll pour récupérer l'access_token (device flow CLI)",
        "description": "Polling endpoint du device flow. Le CLI appelle ce endpoint avec\nson `device_code` jusqu'à recevoir un access_token (succès) ou\nune erreur définitive.\n\nCodes d'erreur (compatibles RFC 6749 §5.2) :\n- `authorization_pending` (400) : pas encore approuvé, retry\n- `expired_token` (400) : device_code expiré ou inconnu → recommencer le flow\n- `access_denied` (400) : session révoquée entre l'approval et le pickup\n- `already_consumed` (410) : token déjà ramassé par un précédent poll → NE PAS retry\n",
        "tags": [
          "CLI Auth"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "device_code"
                ],
                "properties": {
                  "device_code": {
                    "type": "string",
                    "description": "Le secret reçu lors du `start`"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Approval réussie — token retourné",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "access_token",
                    "token_type"
                  ],
                  "properties": {
                    "access_token": {
                      "type": "string",
                      "description": "Personal Access Token (PAT) à utiliser comme\n`Authorization: Bearer <access_token>` pour toute\nrequête API. Visible et révocable dans\n`/profile/personal-access-tokens`.\n"
                    },
                    "token_type": {
                      "type": "string",
                      "example": "Bearer"
                    },
                    "expires_in": {
                      "type": "integer",
                      "description": "Durée de validité en secondes (30 jours)"
                    },
                    "user": {
                      "type": "object",
                      "description": "Infos basiques du user authentifié",
                      "properties": {
                        "id": {
                          "type": "string",
                          "format": "uuid"
                        },
                        "email_address": {
                          "type": "string"
                        },
                        "first_name": {
                          "type": "string"
                        },
                        "last_name": {
                          "type": "string"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "- `error: invalid_request` — device_code manquant\n- `error: authorization_pending` — retry plus tard\n- `error: expired_token` — recommencer le flow\n- `error: access_denied` — session révoquée\n",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "error_description": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "410": {
            "description": "`error: already_consumed` — token déjà ramassé. Le CLI ne doit\nPAS retry, il faut recommencer un device flow.\n",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "error_description": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "TokenExchangeRequest": {
        "type": "object",
        "description": "Requête d'échange de token JWT Edifice",
        "required": [
          "grant_type",
          "assertion",
          "client_id",
          "client_secret"
        ],
        "properties": {
          "grant_type": {
            "type": "string",
            "enum": [
              "urn:ietf:params:oauth:grant-type:jwt-bearer"
            ],
            "description": "Type de grant OAuth2 pour l'échange JWT (RFC 7523)",
            "example": "urn:ietf:params:oauth:grant-type:jwt-bearer"
          },
          "assertion": {
            "type": "string",
            "description": "JWT signé par Edifice contenant les informations utilisateur",
            "example": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImVkaWZpY2Uta2V5LTEifQ.eyJzdWIiOiJ1c2VyLTEyMyIsImVtYWlsIjoidXNlckBlZGlmaWNlLmNvbSIsImZpcnN0X25hbWUiOiJKb2huIiwibGFzdF9uYW1lIjoiRG9lIiwiaXNzIjoiZWRpZmljZSIsImF1ZCI6ImNvcmV4IiwiZXhwIjoxNzA1NDEzNjAwfQ.signature"
          },
          "client_id": {
            "type": "string",
            "description": "Identifiant de l'application OAuth2",
            "example": "Q0OEEVE5kLDll1uAek5B-rJcXJCtkIoEdPwLvDiQ888"
          },
          "client_secret": {
            "type": "string",
            "description": "Secret de l'application OAuth2",
            "example": "jnZuQF8PFv42WmA5vLLBdPrhBAwAqLuBTERGMUERUPE"
          }
        }
      },
      "TokenExchangeResponse": {
        "type": "object",
        "description": "Réponse d'échange de token JWT Edifice",
        "required": [
          "access_token",
          "token_type",
          "expires_in",
          "created_at"
        ],
        "properties": {
          "access_token": {
            "type": "string",
            "description": "Token d'accès Bearer pour authentifier les requêtes API",
            "example": "abc123def456ghi789..."
          },
          "token_type": {
            "type": "string",
            "description": "Type de token (toujours \"Bearer\")",
            "enum": [
              "Bearer"
            ],
            "example": "Bearer"
          },
          "expires_in": {
            "type": "integer",
            "description": "Durée de validité du token en secondes",
            "example": 7200
          },
          "created_at": {
            "type": "integer",
            "description": "Timestamp Unix de création du token",
            "example": 1705410000
          }
        }
      },
      "OAuthError": {
        "type": "object",
        "description": "Réponse d'erreur OAuth2 (RFC 6749)",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "string",
            "description": "Code d'erreur OAuth2",
            "enum": [
              "invalid_request",
              "invalid_client",
              "invalid_grant",
              "unauthorized_client",
              "unsupported_grant_type",
              "server_error"
            ],
            "example": "invalid_grant"
          },
          "error_description": {
            "type": "string",
            "description": "Description détaillée de l'erreur",
            "example": "JWT verification failed: signature invalid"
          }
        }
      },
      "Error": {
        "type": "object",
        "required": [
          "error",
          "message"
        ],
        "properties": {
          "error": {
            "type": "string",
            "description": "Type d'erreur",
            "example": "validation_error"
          },
          "message": {
            "type": "string",
            "description": "Message d'erreur lisible",
            "example": "The request contains invalid parameters"
          },
          "details": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Détails supplémentaires de l'erreur",
            "example": [
              "Name is required",
              "Workspace ID must be a valid integer"
            ]
          }
        }
      },
      "TokenResponse": {
        "type": "object",
        "description": "OAuth2 Token Response",
        "properties": {
          "access_token": {
            "type": "string",
            "description": "Token d'accès Bearer",
            "example": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
          },
          "token_type": {
            "type": "string",
            "description": "Type de token (toujours \"Bearer\" pour Doorkeeper)",
            "example": "Bearer"
          },
          "expires_in": {
            "type": "integer",
            "description": "Durée de validité du token en secondes",
            "example": 7200
          },
          "refresh_token": {
            "type": "string",
            "description": "Jeton de rafraîchissement (si applicable, selon la configuration Doorkeeper)",
            "example": "def50200cde4321cfa9..."
          },
          "scope": {
            "type": "string",
            "description": "Portée accordée (peut être différente de celle demandée)",
            "example": "read write"
          },
          "created_at": {
            "type": "integer",
            "description": "Timestamp Unix de création du token",
            "example": 1640995200
          }
        }
      }
    },
    "securitySchemes": {
      "OAuth2Password": {
        "type": "oauth2",
        "description": "Flow password pour applications de confiance (mobile, CLI, tests)",
        "flows": {
          "password": {
            "tokenUrl": "/oauth/token",
            "scopes": {
              "read": "Lecture des ressources",
              "write": "Modification des ressources",
              "admin": "Accès administratif"
            }
          }
        }
      },
      "OAuth2AuthCode": {
        "type": "oauth2",
        "description": "Flow authorization code pour applications frontend/tierce",
        "flows": {
          "authorizationCode": {
            "authorizationUrl": "/oauth/authorize",
            "tokenUrl": "/oauth/token",
            "scopes": {
              "read": "Lecture des ressources",
              "write": "Modification des ressources",
              "admin": "Accès administratif"
            }
          }
        }
      }
    },
    "responses": {
      "BadRequest": {
        "description": "Requête incorrecte - paramètres invalides",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "error": "bad_request",
              "message": "Paramètres de requête invalides"
            }
          }
        }
      },
      "Unauthorized": {
        "description": "Authentification requise",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "error": "unauthorized",
              "message": "Authentification requise"
            }
          }
        }
      },
      "InternalError": {
        "description": "Erreur interne du serveur",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "error": "internal_error",
              "message": "Une erreur inattendue s'est produite"
            }
          }
        }
      }
    }
  }
}