{
  "openapi": "3.0.3",
  "info": {
    "title": "Identity API",
    "version": "0.1.0",
    "description": "Cadastro, confirmação, login com access token e perfil. Sessão persistida e refresh temporariamente retirados. Criação administrativa de tenants disponível somente no serviço."
  },
  "servers": [
    {
      "url": "/",
      "description": "Raiz da API; rotas do domínio IdP usam o prefixo /identity"
    }
  ],
  "paths": {
    "/identity/register": {
      "post": {
        "operationId": "register",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "email",
                  "password"
                ],
                "properties": {
                  "email": {
                    "type": "string",
                    "format": "email",
                    "maxLength": 254
                  },
                  "password": {
                    "type": "string",
                    "writeOnly": true,
                    "maxLength": 128,
                    "minLength": 15
                  },
                  "name": {
                    "type": "string",
                    "maxLength": 200
                  }
                },
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Cadastro novo ou existente; sem identificadores ou token",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "message"
                  ],
                  "properties": {
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Entrada inválida",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "415": {
            "description": "Content-Type deve ser application/json",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Dependência indisponível",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/identity/confirm-email": {
      "post": {
        "operationId": "confirmEmail",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "token"
                ],
                "properties": {
                  "token": {
                    "type": "string",
                    "minLength": 43,
                    "maxLength": 43
                  }
                },
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "204": {
            "description": "Email confirmado; sem emissão de JWT"
          },
          "400": {
            "description": "Entrada inválida",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "415": {
            "description": "Content-Type deve ser application/json",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Dependência indisponível",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "get": {
        "summary": "Confirmar e-mail pelo link recebido",
        "description": "Consome o token de uso único e redireciona para a home do aplicativo.",
        "operationId": "confirmEmailLink",
        "parameters": [
          {
            "name": "token",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 43,
              "maxLength": 43
            }
          }
        ],
        "responses": {
          "303": {
            "description": "Confirmado; redireciona para https://app.usemetro.com.br/",
            "headers": {
              "Location": {
                "schema": {
                  "type": "string",
                  "format": "uri"
                }
              }
            }
          },
          "400": {
            "description": "Token inválido, expirado ou já consumido"
          },
          "503": {
            "description": "Serviço indisponível"
          }
        }
      }
    },
    "/login": {
      "post": {
        "operationId": "login",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "email",
                  "password"
                ],
                "properties": {
                  "email": {
                    "type": "string",
                    "format": "email",
                    "maxLength": 254
                  },
                  "password": {
                    "type": "string",
                    "writeOnly": true,
                    "maxLength": 128
                  }
                },
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Access token JWT",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TokenResponse"
                }
              }
            }
          },
          "400": {
            "description": "Entrada inválida",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Credencial/token inválido",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "Email pendente ou Origin não autorizado",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "415": {
            "description": "Content-Type deve ser application/json",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Dependência indisponível",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        },
        "summary": "Autenticar e emitir access token",
        "description": "Retorna apenas JWT de 900 segundos; sem refresh, sessão persistida ou cookie. Após expiração, fazer novo login."
      }
    },
    "/me": {
      "get": {
        "operationId": "me",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "description": "O gateway valida o access token do cliente e autoriza a chamada. O serviço aceita somente o contexto assinado X-Metro-Authorization, com sub e access_entity_id. Chamadas diretas com bearer são rejeitadas.",
        "responses": {
          "200": {
            "description": "Dados cadastrais da legal_entity",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LegalEntity"
                }
              }
            }
          },
          "401": {
            "description": "Credencial/token inválido",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Dependência indisponível",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/identity/admin/tenants": {
      "post": {
        "tags": [
          "Admin IdP"
        ],
        "operationId": "createTenant",
        "summary": "Cria tenant com owner e profile compartilhado",
        "description": "Disponível no serviço; não publicado no Kong. Exige contexto assinado pelo gateway. Cria tenant e vínculo de owner atomicamente; usa a única definição system owner, sem cópia por tenant. Owner deve ser pessoa cadastrada e ativa. Não aceita scopes/profiles no payload.",
        "x-internal": true,
        "security": [
          {
            "GatewayAssertion": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateTenantInput"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Tenant e vínculo criados",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TenantCreation"
                }
              }
            }
          },
          "400": {
            "description": "Entrada inválida ou owner inexistente/inativo"
          },
          "401": {
            "description": "Contexto de gateway ausente ou inválido"
          },
          "409": {
            "description": "Colisão de identificadores"
          },
          "415": {
            "description": "Content-Type deve ser application/json"
          },
          "503": {
            "description": "Falha de persistência ou definição owner incompatível/revogada"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "JWT"
      },
      "GatewayAssertion": {
        "type": "apiKey",
        "in": "header",
        "name": "X-Metro-Authorization",
        "description": "JWT RS256 assinado pelo gateway, vinculado a método, path, query e request ID; não é o access token do cliente."
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "required": [
          "error",
          "message",
          "details",
          "request_id"
        ],
        "properties": {
          "error": {
            "type": "string",
            "pattern": "^srn:[a-z_]+$",
            "example": "srn:invalid_input"
          },
          "message": {
            "type": "string",
            "description": "Mensagem segura para o consumidor."
          },
          "details": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ErrorDetail"
            }
          },
          "request_id": {
            "type": "string",
            "description": "ID de correlação do serviço, também retornado em X-Request-ID."
          }
        },
        "additionalProperties": false,
        "example": {
          "error": "srn:invalid_input",
          "message": "Revise os campos informados.",
          "details": [
            {
              "field": "owner_id",
              "code": "invalid_format",
              "message": "Use o UUID canônico minúsculo da entidade legal, sem prefixo."
            }
          ],
          "request_id": "example-request-id"
        }
      },
      "LegalEntity": {
        "type": "object",
        "required": [
          "id",
          "name",
          "photo_url",
          "status",
          "created_at"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "photo_url": {
            "type": "string",
            "nullable": true
          },
          "status": {
            "type": "string",
            "enum": [
              "active"
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "CreateTenantInput": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "name",
          "owner_id"
        ],
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 200
          },
          "owner_id": {
            "type": "string",
            "format": "uuid",
            "description": "ID de pessoa global já cadastrada e ativa, em UUID canônico minúsculo."
          }
        }
      },
      "TenantCreation": {
        "type": "object",
        "required": [
          "tenant",
          "owner_profile"
        ],
        "properties": {
          "tenant": {
            "type": "object",
            "required": [
              "id",
              "name",
              "owner_id",
              "status",
              "created_at"
            ],
            "properties": {
              "id": {
                "type": "string",
                "pattern": "^tenant:[0-9a-f]{8}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$",
                "description": "Identificador completo do tenant com UUIDv7 (RFC 9562)."
              },
              "name": {
                "type": "string"
              },
              "owner_id": {
                "type": "string",
                "format": "uuid"
              },
              "status": {
                "type": "string",
                "enum": [
                  "active"
                ]
              },
              "created_at": {
                "type": "string",
                "format": "date-time"
              }
            }
          },
          "owner_profile": {
            "type": "object",
            "required": [
              "id",
              "name",
              "scopes"
            ],
            "properties": {
              "id": {
                "type": "string",
                "enum": [
                  "00000000-0000-4000-8000-000000000001"
                ]
              },
              "name": {
                "type": "string",
                "enum": [
                  "owner"
                ]
              },
              "scopes": {
                "type": "array",
                "items": {
                  "type": "string"
                },
                "description": "Definição global compartilhada: tenant:read, tenant:members:manage, tenant:profiles:manage; sem permissões implícitas sobre outros recursos."
              }
            }
          }
        }
      },
      "TokenResponse": {
        "type": "object",
        "required": [
          "access_token",
          "token_type",
          "expires_in"
        ],
        "properties": {
          "access_token": {
            "type": "string"
          },
          "token_type": {
            "type": "string",
            "enum": [
              "Bearer"
            ]
          },
          "expires_in": {
            "type": "integer",
            "enum": [
              900
            ]
          }
        },
        "additionalProperties": false,
        "description": "Access token JWT de 900 segundos, sem estado de sessão ou refresh."
      },
      "ErrorDetail": {
        "type": "object",
        "required": [
          "field",
          "code",
          "message"
        ],
        "properties": {
          "field": {
            "type": "string",
            "description": "Campo inválido; body representa o corpo completo."
          },
          "code": {
            "type": "string",
            "description": "Categoria estável de validação."
          },
          "message": {
            "type": "string",
            "description": "Orientação sem incluir valores enviados."
          }
        },
        "additionalProperties": false
      }
    }
  }
}
