{
  "info": {
    "_postman_id": "enxu-back-api-collection",
    "name": "Nexus API - enxu_back",
    "description": "Colección completa de endpoints de la API Nexus (enxu_back). Organizada por módulos: Auth, Password, Perfil, Perfil Profesional, Ubicaciones, Proyectos y Chat.\n\n## Configuración inicial\n1. Importa esta colección en Postman.\n2. Crea un environment con las variables `base_url` y `token`.\n3. Realiza un POST a `/auth/login` y copia el token recibido en la variable `{{token}}`.\n4. Todos los endpoints protegidos usarán ese token automáticamente.",
    "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
  },
  "variable": [
    {
      "key": "base_url",
      "value": "http://localhost/api-nexus/public",
      "type": "string"
    },
    {
      "key": "token",
      "value": "",
      "type": "string"
    }
  ],
  "item": [
    {
      "name": "Root",
      "item": [
        {
          "name": "Health Check",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/",
              "host": ["{{base_url}}"],
              "path": [""]
            },
            "description": "Verifica que la API esté funcionando correctamente. Retorna la versión y entorno actual."
          },
          "response": [
            {
              "name": "200 OK",
              "originalRequest": {
                "method": "GET",
                "url": { "raw": "{{base_url}}/" }
              },
              "status": "OK",
              "code": 200,
              "body": "{\n  \"success\": true,\n  \"message\": \"API Nexus funcionando correctamente\",\n  \"data\": {\n    \"version\": \"v1\",\n    \"entorno\": \"development\"\n  }\n}"
            }
          ]
        }
      ]
    },
    {
      "name": "Auth",
      "description": "Módulo de autenticación. Gestiona login, registro, autenticación social, verificación de sesión y logout.",
      "item": [
        {
          "name": "Login",
          "request": {
            "method": "POST",
            "header": [
              { "key": "Content-Type", "value": "application/json" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"email\": \"usuario@ejemplo.com\",\n  \"password\": \"MiPassword123!\",\n  \"device_uuid\": \"550e8400-e29b-41d4-a716-446655440000\",\n  \"device_sistema\": \"Android\",\n  \"device_modelo\": \"Samsung Galaxy S21\"\n}",
              "options": { "raw": { "language": "json" } }
            },
            "url": {
              "raw": "{{base_url}}/auth/login",
              "host": ["{{base_url}}"],
              "path": ["auth", "login"]
            },
            "description": "Autentica un usuario con email y contraseña. Requiere información del dispositivo. Retorna un token JWT."
          },
          "response": [
            {
              "name": "200 Login exitoso",
              "status": "OK",
              "code": 200,
              "body": "{\n  \"success\": true,\n  \"message\": \"Login exitoso\",\n  \"data\": {\n    \"token\": \"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...\",\n    \"usuario\": {\n      \"usu_id\": 1,\n      \"usu_email\": \"usuario@ejemplo.com\",\n      \"usu_estado\": 1,\n      \"usu_verificado\": 1\n    }\n  }\n}"
            },
            {
              "name": "400 Campos requeridos",
              "status": "Bad Request",
              "code": 400,
              "body": "{\n  \"success\": false,\n  \"message\": \"Email, contraseña y device_uuid son requeridos\"\n}"
            },
            {
              "name": "401 Credenciales inválidas",
              "status": "Unauthorized",
              "code": 401,
              "body": "{\n  \"success\": false,\n  \"message\": \"Credenciales inválidas\"\n}"
            }
          ]
        },
        {
          "name": "Registrar",
          "request": {
            "method": "POST",
            "header": [
              { "key": "Content-Type", "value": "application/json" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"email\": \"nuevo@ejemplo.com\",\n  \"password\": \"MiPassword123!\",\n  \"device_uuid\": \"550e8400-e29b-41d4-a716-446655440001\",\n  \"device_sistema\": \"iOS\",\n  \"device_modelo\": \"iPhone 13\"\n}",
              "options": { "raw": { "language": "json" } }
            },
            "url": {
              "raw": "{{base_url}}/auth/registrar",
              "host": ["{{base_url}}"],
              "path": ["auth", "registrar"]
            },
            "description": "Registra un nuevo usuario. Requiere email, contraseña y datos del dispositivo. Retorna el token JWT y datos del usuario creado."
          },
          "response": [
            {
              "name": "201 Usuario registrado",
              "status": "Created",
              "code": 201,
              "body": "{\n  \"success\": true,\n  \"message\": \"Usuario registrado exitosamente\",\n  \"data\": {\n    \"token\": \"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...\",\n    \"usuario\": {\n      \"usu_id\": 42,\n      \"usu_email\": \"nuevo@ejemplo.com\",\n      \"usu_estado\": 1,\n      \"usu_verificado\": 0\n    }\n  }\n}"
            },
            {
              "name": "400 Email ya registrado",
              "status": "Bad Request",
              "code": 400,
              "body": "{\n  \"success\": false,\n  \"message\": \"El email ya está registrado\"\n}"
            }
          ]
        },
        {
          "name": "Login con Google",
          "request": {
            "method": "POST",
            "header": [
              { "key": "Content-Type", "value": "application/json" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"id_token\": \"eyJhbGciOiJSUzI1NiIsImtpZCI6...\",\n  \"email\": \"usuario@gmail.com\",\n  \"device_uuid\": \"550e8400-e29b-41d4-a716-446655440002\",\n  \"device_sistema\": \"Android\",\n  \"device_modelo\": \"Pixel 6\"\n}",
              "options": { "raw": { "language": "json" } }
            },
            "url": {
              "raw": "{{base_url}}/auth/login-google",
              "host": ["{{base_url}}"],
              "path": ["auth", "login-google"]
            },
            "description": "Autentica o registra un usuario mediante Google OAuth. Requiere el id_token de Google, email verificado y datos del dispositivo."
          },
          "response": [
            {
              "name": "200 Login Google exitoso",
              "status": "OK",
              "code": 200,
              "body": "{\n  \"success\": true,\n  \"message\": \"Login con Google exitoso\",\n  \"data\": {\n    \"token\": \"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...\",\n    \"usuario\": {\n      \"usu_id\": 7,\n      \"usu_email\": \"usuario@gmail.com\",\n      \"usu_estado\": 1,\n      \"usu_verificado\": 1\n    }\n  }\n}"
            },
            {
              "name": "400 Token de Google inválido",
              "status": "Bad Request",
              "code": 400,
              "body": "{\n  \"success\": false,\n  \"message\": \"Token de Google, email y device_uuid son requeridos\"\n}"
            }
          ]
        },
        {
          "name": "Login con Facebook",
          "request": {
            "method": "POST",
            "header": [
              { "key": "Content-Type", "value": "application/json" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"access_token\": \"EAABwzLixnjYBO...\",\n  \"email\": \"usuario@facebook.com\",\n  \"device_uuid\": \"550e8400-e29b-41d4-a716-446655440003\",\n  \"device_sistema\": \"iOS\",\n  \"device_modelo\": \"iPhone 14\"\n}",
              "options": { "raw": { "language": "json" } }
            },
            "url": {
              "raw": "{{base_url}}/auth/login-facebook",
              "host": ["{{base_url}}"],
              "path": ["auth", "login-facebook"]
            },
            "description": "Autentica o registra un usuario mediante Facebook OAuth. Requiere el access_token de Facebook, email y datos del dispositivo."
          },
          "response": [
            {
              "name": "200 Login Facebook exitoso",
              "status": "OK",
              "code": 200,
              "body": "{\n  \"success\": true,\n  \"message\": \"Login con Facebook exitoso\",\n  \"data\": {\n    \"token\": \"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...\",\n    \"usuario\": {\n      \"usu_id\": 12,\n      \"usu_email\": \"usuario@facebook.com\"\n    }\n  }\n}"
            }
          ]
        },
        {
          "name": "Verificar Sesión",
          "request": {
            "method": "GET",
            "header": [
              { "key": "Authorization", "value": "Bearer {{token}}" }
            ],
            "url": {
              "raw": "{{base_url}}/auth/verificar-sesion",
              "host": ["{{base_url}}"],
              "path": ["auth", "verificar-sesion"]
            },
            "description": "Verifica que la sesión actual (token JWT) sea válida y esté activa en base de datos. Requiere autenticación."
          },
          "response": [
            {
              "name": "200 Sesión válida",
              "status": "OK",
              "code": 200,
              "body": "{\n  \"success\": true,\n  \"message\": \"Sesión válida\",\n  \"data\": {\n    \"usu_id\": 1,\n    \"usu_email\": \"usuario@ejemplo.com\",\n    \"sesion_activa\": true\n  }\n}"
            },
            {
              "name": "401 Token inválido",
              "status": "Unauthorized",
              "code": 401,
              "body": "{\n  \"success\": false,\n  \"message\": \"Token inválido o expirado\"\n}"
            }
          ]
        },
        {
          "name": "Logout",
          "request": {
            "method": "POST",
            "header": [
              { "key": "Authorization", "value": "Bearer {{token}}" },
              { "key": "Content-Type", "value": "application/json" }
            ],
            "url": {
              "raw": "{{base_url}}/auth/logout",
              "host": ["{{base_url}}"],
              "path": ["auth", "logout"]
            },
            "description": "Cierra la sesión invalidando el token JWT actual en base de datos. Requiere autenticación."
          },
          "response": [
            {
              "name": "200 OK",
              "status": "OK",
              "code": 200,
              "body": "{\n  \"success\": true,\n  \"message\": \"Logout exitoso\",\n  \"data\": null\n}"
            }
          ]
        },
        {
          "name": "Verificar OTP",
          "request": {
            "method": "POST",
            "header": [
              { "key": "Content-Type", "value": "application/json" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"email\": \"usuario@ejemplo.com\",\n  \"codigo\": \"1234\"\n}",
              "options": { "raw": { "language": "json" } }
            },
            "url": {
              "raw": "{{base_url}}/auth/verificar-otp",
              "host": ["{{base_url}}"],
              "path": ["auth", "verificar-otp"]
            },
            "description": "Verifica el código OTP enviado por email después del registro. Una vez verificado, el usuario puede hacer login."
          },
          "response": [
            {
              "name": "200 OK",
              "status": "OK",
              "code": 200,
              "body": "{\n  \"success\": true,\n  \"message\": \"Código verificado exitosamente\",\n  \"data\": {\n    \"verificacion_exitosa\": true,\n    \"usuario_id\": 1,\n    \"tipo_verificacion\": \"REGISTRO\",\n    \"fecha_verificacion\": \"2026-03-17 20:30:00\"\n  }\n}"
            },
            {
              "name": "400 Código inválido",
              "status": "Bad Request",
              "code": 400,
              "body": "{\n  \"success\": false,\n  \"message\": \"Código incorrecto\"\n}"
            },
            {
              "name": "400 Código expirado",
              "status": "Bad Request",
              "code": 400,
              "body": "{\n  \"success\": false,\n  \"message\": \"El código ha expirado. Solicita uno nuevo\"\n}"
            }
          ]
        },
        {
          "name": "Reenviar OTP",
          "request": {
            "method": "POST",
            "header": [
              { "key": "Content-Type", "value": "application/json" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"email\": \"usuario@ejemplo.com\"\n}",
              "options": { "raw": { "language": "json" } }
            },
            "url": {
              "raw": "{{base_url}}/auth/reenviar-otp",
              "host": ["{{base_url}}"],
              "path": ["auth", "reenviar-otp"]
            },
            "description": "Reenvía un nuevo código OTP al email del usuario. Tiene rate limiting de 1 minuto entre reenvíos."
          },
          "response": [
            {
              "name": "200 OK",
              "status": "OK",
              "code": 200,
              "body": "{\n  \"success\": true,\n  \"message\": \"Código reenviado exitosamente\",\n  \"data\": {\n    \"codigo_generado\": true,\n    \"email_enviado\": true,\n    \"expira_en_minutos\": 10,\n    \"email_destino\": \"us****@ejemplo.com\"\n  }\n}"
            },
            {
              "name": "400 Rate limit",
              "status": "Bad Request",
              "code": 400,
              "body": "{\n  \"success\": false,\n  \"message\": \"Debes esperar 45 segundos antes de solicitar otro código\"\n}"
            }
          ]
        }
      ]
    },
    {
      "name": "Password",
      "description": "Módulo de recuperación de contraseña. Permite solicitar un enlace de reset, validar el token y establecer nueva contraseña.",
      "item": [
        {
          "name": "Solicitar Recuperación",
          "request": {
            "method": "POST",
            "header": [
              { "key": "Content-Type", "value": "application/json" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"email\": \"usuario@ejemplo.com\"\n}",
              "options": { "raw": { "language": "json" } }
            },
            "url": {
              "raw": "{{base_url}}/password/solicitar",
              "host": ["{{base_url}}"],
              "path": ["password", "solicitar"]
            },
            "description": "Envía un correo con enlace de recuperación de contraseña. Por seguridad, siempre responde 202 aunque el email no exista."
          },
          "response": [
            {
              "name": "202 Solicitud procesada",
              "status": "Accepted",
              "code": 202,
              "body": "{\n  \"success\": true,\n  \"message\": \"Se ha enviado un enlace de recuperación si el email existe\",\n  \"data\": null\n}"
            },
            {
              "name": "400 Email requerido",
              "status": "Bad Request",
              "code": 400,
              "body": "{\n  \"success\": false,\n  \"message\": \"El email es requerido\"\n}"
            }
          ]
        },
        {
          "name": "Validar Token de Reset",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/password/validar?token=abc123tokenreset",
              "host": ["{{base_url}}"],
              "path": ["password", "validar"],
              "query": [
                { "key": "token", "value": "abc123tokenreset", "description": "Token de recuperación recibido por email (requerido)" }
              ]
            },
            "description": "Valida que un token de recuperación de contraseña sea válido y no haya expirado."
          },
          "response": [
            {
              "name": "200 Token válido",
              "status": "OK",
              "code": 200,
              "body": "{\n  \"success\": true,\n  \"message\": \"Token válido\",\n  \"data\": {\n    \"token\": \"abc123tokenreset\",\n    \"email\": \"usuario@ejemplo.com\",\n    \"expira\": \"2025-03-13T23:59:59\"\n  }\n}"
            },
            {
              "name": "400 Token inválido o expirado",
              "status": "Bad Request",
              "code": 400,
              "body": "{\n  \"success\": false,\n  \"message\": \"Token inválido o expirado\"\n}"
            }
          ]
        },
        {
          "name": "Restablecer Contraseña",
          "request": {
            "method": "POST",
            "header": [
              { "key": "Content-Type", "value": "application/json" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"token\": \"abc123tokenreset\",\n  \"password\": \"NuevaPassword123!\",\n  \"confirmacion\": \"NuevaPassword123!\"\n}",
              "options": { "raw": { "language": "json" } }
            },
            "url": {
              "raw": "{{base_url}}/password/restablecer",
              "host": ["{{base_url}}"],
              "path": ["password", "restablecer"]
            },
            "description": "Establece una nueva contraseña usando el token de recuperación. El token se invalida tras su uso."
          },
          "response": [
            {
              "name": "200 Contraseña restablecida",
              "status": "OK",
              "code": 200,
              "body": "{\n  \"success\": true,\n  \"message\": \"Contraseña restablecida correctamente\"\n}"
            },
            {
              "name": "400 Campos requeridos",
              "status": "Bad Request",
              "code": 400,
              "body": "{\n  \"success\": false,\n  \"message\": \"Token, password y confirmación son requeridos\"\n}"
            }
          ]
        }
      ]
    },
    {
      "name": "Perfil",
      "description": "Gestión del perfil personal del usuario autenticado: datos personales, resumen completo y habilidades.",
      "item": [
        {
          "name": "Guardar / Actualizar Perfil",
          "request": {
            "method": "POST",
            "header": [
              { "key": "Authorization", "value": "Bearer {{token}}" },
              { "key": "Content-Type", "value": "application/json" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"per_nombres\": \"Juan Carlos\",\n  \"per_ape_paterno\": \"Rodríguez\",\n  \"per_ape_materno\": \"López\",\n  \"per_genero\": \"M\",\n  \"per_empresa\": \"Tech Solutions S.A.\",\n  \"ppf_id\": 2,\n  \"per_idioma\": \"es\",\n  \"per_tema\": \"claro\",\n  \"per_titulo\": \"Desarrollador Full Stack\",\n  \"per_descripcion\": \"Desarrollador con 5 años de experiencia en tecnologías web.\",\n  \"per_remoto\": 1,\n  \"per_disponibilidad\": 1,\n  \"ciu_id\": 5,\n  \"imagen\": \"data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD...\",\n  \"eliminar_imagen\": false\n}",
              "options": { "raw": { "language": "json" } }
            },
            "url": {
              "raw": "{{base_url}}/perfil",
              "host": ["{{base_url}}"],
              "path": ["perfil"]
            },
            "description": "Crea o actualiza el perfil personal del usuario autenticado. Todos los campos son opcionales en actualización. El campo `imagen` acepta base64 cuando el body es JSON (modo raw). Para enviar un archivo físico, cambia el body a form-data y agrega el campo `imagen` como file; también puedes enviar `eliminar_imagen=true` para borrar la portada actual."
          },
          "response": [
            {
              "name": "200 Perfil actualizado",
              "status": "OK",
              "code": 200,
              "body": "{\n  \"success\": true,\n  \"message\": \"Perfil actualizado exitosamente\",\n  \"data\": {\n    \"per_id\": 1,\n    \"usu_id\": 1,\n    \"per_nombres\": \"Juan Carlos\",\n    \"per_ape_paterno\": \"Rodríguez\",\n    \"per_ape_materno\": \"López\",\n    \"per_genero\": \"M\",\n    \"per_empresa\": \"Tech Solutions S.A.\",\n    \"ppf_id\": 2,\n    \"per_idioma\": \"es\",\n    \"per_tema\": \"claro\",\n    \"per_titulo\": \"Desarrollador Full Stack\",\n    \"per_descripcion\": \"Desarrollador con 5 años de experiencia en tecnologías web.\",\n    \"per_imagen\": \"uploads/perfil/archivo_abc123.jpg\",\n    \"per_remoto\": 1,\n    \"per_disponibilidad\": 1,\n    \"ciu_id\": 5\n  }\n}"
            },
            {
              "name": "201 Perfil creado",
              "status": "Created",
              "code": 201,
              "body": "{\n  \"success\": true,\n  \"message\": \"Perfil creado exitosamente\",\n  \"data\": {\n    \"per_id\": 10,\n    \"usu_id\": 1\n  }\n}"
            },
            {
              "name": "401 No autenticado",
              "status": "Unauthorized",
              "code": 401,
              "body": "{\n  \"success\": false,\n  \"message\": \"Token no proporcionado\"\n}"
            }
          ]
        },
        {
          "name": "Resumen de Perfil",
          "request": {
            "method": "GET",
            "header": [
              { "key": "Authorization", "value": "Bearer {{token}}" }
            ],
            "url": {
              "raw": "{{base_url}}/perfil/resumen",
              "host": ["{{base_url}}"],
              "path": ["perfil", "resumen"]
            },
            "description": "Obtiene el resumen completo del perfil del usuario: datos personales, perfil profesional, métricas, conexiones y habilidades."
          },
          "response": [
            {
              "name": "200 Resumen obtenido",
              "status": "OK",
              "code": 200,
              "body": "{\n  \"success\": true,\n  \"message\": \"Resumen de perfil obtenido correctamente\",\n  \"data\": {\n    \"usuario\": {\n      \"usu_id\": 1,\n      \"email\": \"usuario@ejemplo.com\",\n      \"verificado\": true\n    },\n    \"perfil\": {\n      \"nombres_completos\": \"Juan Carlos Rodríguez López\",\n      \"titulo_profesional\": \"Desarrollador Backend\",\n      \"descripcion\": \"Especialista en PHP y APIs REST\",\n      \"ciudad_id\": 5\n    },\n    \"perfil_profesional\": {\n      \"ppf_id\": 2,\n      \"ppf_nombre\": \"Desarrollador Backend\",\n      \"ppf_descripcion\": \"Especialista en desarrollo de servidores\",\n      \"ppf_estado\": 1\n    },\n    \"portafolios\": [\n      {\n        \"por_id\": 15,\n        \"por_empresa\": \"Globant\",\n        \"por_puesto\": \"Senior Backend\",\n        \"por_descripcion\": \"Responsable del diseño de microservicios en Laravel\",\n        \"por_imagen\": \"uploads/portafolio/archivo_xyz.jpg\",\n        \"por_estado\": 1\n      },\n      {\n        \"por_id\": 12,\n        \"por_empresa\": \"Freelance\",\n        \"por_puesto\": \"Consultor\",\n        \"por_descripcion\": \"Implementación de APIs para fintech regional\",\n        \"por_imagen\": null,\n        \"por_estado\": 1\n      }\n    ],\n    \"metricas\": {\n      \"proyectos_creados\": 3,\n      \"proyectos_participa\": 5,\n      \"total_conexiones\": 12\n    },\n    \"conexiones\": [],\n    \"habilidades\": [\n      { \"hab_id\": 1, \"hab_nombre\": \"PHP\", \"hab_categoria\": \"Backend\" },\n      { \"hab_id\": 3, \"hab_nombre\": \"MySQL\", \"hab_categoria\": \"Base de Datos\" }\n    ]\n  }\n}"
            },
            {
              "name": "404 Usuario no encontrado",
              "status": "Not Found",
              "code": 404,
              "body": "{\n  \"success\": false,\n  \"message\": \"Usuario no encontrado\"\n}"
            }
          ]
        },
        {
          "name": "Listar Habilidades del Perfil",
          "request": {
            "method": "GET",
            "header": [
              { "key": "Authorization", "value": "Bearer {{token}}" }
            ],
            "url": {
              "raw": "{{base_url}}/perfil/habilidades",
              "host": ["{{base_url}}"],
              "path": ["perfil", "habilidades"]
            },
            "description": "Obtiene las habilidades asociadas al perfil profesional del usuario autenticado."
          },
          "response": [
            {
              "name": "200 Habilidades obtenidas",
              "status": "OK",
              "code": 200,
              "body": "{\n  \"success\": true,\n  \"message\": \"Habilidades obtenidas correctamente\",\n  \"data\": [\n    {\n      \"hab_id\": 1,\n      \"hab_nombre\": \"PHP\",\n      \"hab_categoria\": \"Backend\"\n    },\n    {\n      \"hab_id\": 5,\n      \"hab_nombre\": \"JavaScript\",\n      \"hab_categoria\": \"Frontend\"\n    }\n  ]\n}"
            }
          ]
        },
        {
          "name": "Actualizar Habilidades del Perfil",
          "request": {
            "method": "PUT",
            "header": [
              { "key": "Authorization", "value": "Bearer {{token}}" },
              { "key": "Content-Type", "value": "application/json" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"habilidades\": [1, 3, 5, 8]\n}",
              "options": { "raw": { "language": "json" } }
            },
            "url": {
              "raw": "{{base_url}}/perfil/habilidades",
              "host": ["{{base_url}}"],
              "path": ["perfil", "habilidades"]
            },
            "description": "Reemplaza completamente las habilidades del perfil del usuario con el nuevo conjunto de IDs de habilidades."
          },
          "response": [
            {
              "name": "200 Habilidades actualizadas",
              "status": "OK",
              "code": 200,
              "body": "{\n  \"success\": true,\n  \"message\": \"Habilidades actualizadas correctamente\",\n  \"data\": [\n    { \"hab_id\": 1, \"hab_nombre\": \"PHP\", \"hab_categoria\": \"Backend\" },\n    { \"hab_id\": 3, \"hab_nombre\": \"MySQL\", \"hab_categoria\": \"Base de Datos\" },\n    { \"hab_id\": 5, \"hab_nombre\": \"JavaScript\", \"hab_categoria\": \"Frontend\" },\n    { \"hab_id\": 8, \"hab_nombre\": \"Docker\", \"hab_categoria\": \"DevOps\" }\n  ]\n}"
            }
          ]
        },
        {
          "name": "Crear Portafolio",
          "request": {
            "method": "POST",
            "header": [
              { "key": "Authorization", "value": "Bearer {{token}}" },
              { "key": "Content-Type", "value": "application/json" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"empresa\": \"Globant\",\n  \"puesto\": \"Senior Backend\",\n  \"descripcion\": \"Lideré el desarrollo de microservicios en Laravel y RabbitMQ.\",\n  \"imagen\": \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUg...\"\n}",
              "options": { "raw": { "language": "json" } }
            },
            "url": {
              "raw": "{{base_url}}/perfil/portafolio",
              "host": ["{{base_url}}"],
              "path": ["perfil", "portafolio"]
            },
            "description": "Crea un nuevo portafolio para el perfil del usuario autenticado. Campos empresa, puesto y descripcion son obligatorios. Se permite enviar base64 o multipart/form-data para la imagen. Máximo 10 portafolios activos."
          },
          "response": [
            {
              "name": "201 Portafolio creado",
              "status": "Created",
              "code": 201,
              "body": "{\n  \"success\": true,\n  \"message\": \"Portafolio creado exitosamente\",\n  \"data\": {\n    \"por_id\": 18,\n    \"per_id\": 3,\n    \"por_empresa\": \"Globant\",\n    \"por_puesto\": \"Senior Backend\",\n    \"por_descripcion\": \"Lideré el desarrollo de microservicios en Laravel y RabbitMQ.\",\n    \"por_imagen\": \"uploads/portafolio/archivo_nuevo.png\",\n    \"por_estado\": 1\n  }\n}"
            },
            {
              "name": "400 Límite alcanzado",
              "status": "Bad Request",
              "code": 400,
              "body": "{\n  \"success\": false,\n  \"message\": \"Solo se permiten hasta 10 portafolios activos\"\n}"
            }
          ]
        },
        {
          "name": "Listar Portafolios del Perfil",
          "request": {
            "method": "GET",
            "header": [
              { "key": "Authorization", "value": "Bearer {{token}}" }
            ],
            "url": {
              "raw": "{{base_url}}/perfil/portafolio?incluir_inactivos=false",
              "host": ["{{base_url}}"],
              "path": ["perfil", "portafolio"],
              "query": [
                { "key": "incluir_inactivos", "value": "false", "description": "Si es true, retorna portafolios inactivos (por_estado=0)." }
              ]
            },
            "description": "Lista los portafolios del perfil del usuario autenticado. Por defecto solo retorna los activos (por_estado=1)."
          },
          "response": [
            {
              "name": "200 Portafolios obtenidos",
              "status": "OK",
              "code": 200,
              "body": "{\n  \"success\": true,\n  \"message\": \"Portafolios obtenidos correctamente\",\n  \"data\": [\n    {\n      \"por_id\": 15,\n      \"per_id\": 3,\n      \"por_empresa\": \"Globant\",\n      \"por_puesto\": \"Senior Backend\",\n      \"por_descripcion\": \"Diseño de microservicios y APIs REST\",\n      \"por_imagen\": \"uploads/portafolio/archivo_xyz.jpg\",\n      \"por_estado\": 1,\n      \"creado_fecha\": \"2026-03-20 10:30:00\"\n    }\n  ]\n}"
            }
          ]
        },
        {
          "name": "Actualizar Portafolio",
          "request": {
            "method": "PUT",
            "header": [
              { "key": "Authorization", "value": "Bearer {{token}}" },
              { "key": "Content-Type", "value": "application/json" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"empresa\": \"Globant\",\n  \"puesto\": \"Tech Lead Backend\",\n  \"descripcion\": \"Coordinación del roadmap técnico y despliegues CI/CD.\",\n  \"estado\": true,\n  \"eliminar_imagen\": false\n}",
              "options": { "raw": { "language": "json" } }
            },
            "url": {
              "raw": "{{base_url}}/perfil/portafolio/18",
              "host": ["{{base_url}}"],
              "path": ["perfil", "portafolio", "18"]
            },
            "description": "Actualiza los datos de un portafolio del usuario autenticado. Se pueden modificar empresa, puesto, descripcion, estado e imagen (base64 o multipart)."
          },
          "response": [
            {
              "name": "200 Portafolio actualizado",
              "status": "OK",
              "code": 200,
              "body": "{\n  \"success\": true,\n  \"message\": \"Portafolio actualizado exitosamente\",\n  \"data\": {\n    \"por_id\": 18,\n    \"por_empresa\": \"Globant\",\n    \"por_puesto\": \"Tech Lead Backend\",\n    \"por_descripcion\": \"Coordinación del roadmap técnico y despliegues CI/CD.\",\n    \"por_imagen\": \"uploads/portafolio/archivo_actualizado.png\",\n    \"por_estado\": 1\n  }\n}"
            }
          ]
        },
        {
          "name": "Eliminar (anular) Portafolio",
          "request": {
            "method": "DELETE",
            "header": [
              { "key": "Authorization", "value": "Bearer {{token}}" }
            ],
            "url": {
              "raw": "{{base_url}}/perfil/portafolio/18",
              "host": ["{{base_url}}"],
              "path": ["perfil", "portafolio", "18"]
            },
            "description": "Realiza una eliminación lógica (por_estado=0) del portafolio indicado."
          },
          "response": [
            {
              "name": "200 Portafolio eliminado",
              "status": "OK",
              "code": 200,
              "body": "{\n  \"success\": true,\n  \"message\": \"Portafolio eliminado correctamente\"\n}"
            }
          ]
        }
      ]
    },
    {
      "name": "Perfil Profesional",
      "description": "Gestión de perfiles profesionales (roles/tipos de usuario como Desarrollador Backend, Diseñador UX, etc.).",
      "item": [
        {
          "name": "Listar Perfiles Profesionales",
          "request": {
            "method": "GET",
            "header": [
              { "key": "Authorization", "value": "Bearer {{token}}" }
            ],
            "url": {
              "raw": "{{base_url}}/perfil-profesional?estado=1&buscar=desarrollador",
              "host": ["{{base_url}}"],
              "path": ["perfil-profesional"],
              "query": [
                { "key": "estado", "value": "1", "description": "Filtrar por estado: 1=activo, 0=inactivo (opcional)" },
                { "key": "buscar", "value": "desarrollador", "description": "Búsqueda por nombre (opcional)" }
              ]
            },
            "description": "Lista todos los perfiles profesionales disponibles. Soporta filtros por estado y búsqueda por nombre."
          },
          "response": [
            {
              "name": "200 Perfiles obtenidos",
              "status": "OK",
              "code": 200,
              "body": "{\n  \"success\": true,\n  \"message\": \"Perfiles profesionales obtenidos correctamente\",\n  \"data\": [\n    {\n      \"ppf_id\": 1,\n      \"ppf_nombre\": \"Desarrollador Frontend\",\n      \"ppf_descripcion\": \"Especialista en interfaces de usuario\",\n      \"ppf_estado\": 1\n    },\n    {\n      \"ppf_id\": 2,\n      \"ppf_nombre\": \"Desarrollador Backend\",\n      \"ppf_descripcion\": \"Especialista en servidor y APIs\",\n      \"ppf_estado\": 1\n    }\n  ]\n}"
            }
          ]
        },
        {
          "name": "Crear Perfil Profesional",
          "request": {
            "method": "POST",
            "header": [
              { "key": "Authorization", "value": "Bearer {{token}}" },
              { "key": "Content-Type", "value": "application/json" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"ppf_nombre\": \"Desarrollador Mobile\",\n  \"ppf_descripcion\": \"Especialista en aplicaciones móviles iOS y Android\"\n}",
              "options": { "raw": { "language": "json" } }
            },
            "url": {
              "raw": "{{base_url}}/perfil-profesional",
              "host": ["{{base_url}}"],
              "path": ["perfil-profesional"]
            },
            "description": "Crea un nuevo perfil profesional en el sistema."
          },
          "response": [
            {
              "name": "201 Perfil profesional creado",
              "status": "Created",
              "code": 201,
              "body": "{\n  \"success\": true,\n  \"message\": \"Perfil profesional creado correctamente\",\n  \"data\": {\n    \"ppf_id\": 5,\n    \"ppf_nombre\": \"Desarrollador Mobile\",\n    \"ppf_descripcion\": \"Especialista en aplicaciones móviles iOS y Android\",\n    \"ppf_estado\": 1\n  }\n}"
            },
            {
              "name": "400 Nombre requerido",
              "status": "Bad Request",
              "code": 400,
              "body": "{\n  \"success\": false,\n  \"message\": \"El nombre del perfil profesional es requerido\"\n}"
            }
          ]
        },
        {
          "name": "Actualizar Perfil Profesional",
          "request": {
            "method": "PUT",
            "header": [
              { "key": "Authorization", "value": "Bearer {{token}}" },
              { "key": "Content-Type", "value": "application/json" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"ppf_nombre\": \"Desarrollador Mobile Senior\",\n  \"ppf_descripcion\": \"Especialista senior en React Native y Flutter\"\n}",
              "options": { "raw": { "language": "json" } }
            },
            "url": {
              "raw": "{{base_url}}/perfil-profesional/5",
              "host": ["{{base_url}}"],
              "path": ["perfil-profesional", "5"]
            },
            "description": "Actualiza los datos de un perfil profesional existente por su ID (ppf_id en el path)."
          },
          "response": [
            {
              "name": "200 Perfil profesional actualizado",
              "status": "OK",
              "code": 200,
              "body": "{\n  \"success\": true,\n  \"message\": \"Perfil profesional actualizado correctamente\",\n  \"data\": {\n    \"ppf_id\": 5,\n    \"ppf_nombre\": \"Desarrollador Mobile Senior\",\n    \"ppf_descripcion\": \"Especialista senior en React Native y Flutter\",\n    \"ppf_estado\": 1\n  }\n}"
            },
            {
              "name": "400 ID inválido",
              "status": "Bad Request",
              "code": 400,
              "body": "{\n  \"success\": false,\n  \"message\": \"Identificador de perfil profesional inválido\"\n}"
            }
          ]
        }
      ]
    },
    {
      "name": "Ubicaciones",
      "description": "Catálogos geográficos: países, departamentos/estados y ciudades. No requieren autenticación.",
      "item": [
        {
          "name": "Listar Países",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/ubicaciones/paises",
              "host": ["{{base_url}}"],
              "path": ["ubicaciones", "paises"]
            },
            "description": "Retorna la lista completa de países disponibles en el sistema."
          },
          "response": [
            {
              "name": "200 Países obtenidos",
              "status": "OK",
              "code": 200,
              "body": "{\n  \"success\": true,\n  \"message\": \"Listado de países\",\n  \"data\": [\n    { \"pai_id\": 1, \"pai_nombre\": \"Colombia\", \"pai_codigo\": \"CO\" },\n    { \"pai_id\": 2, \"pai_nombre\": \"México\", \"pai_codigo\": \"MX\" },\n    { \"pai_id\": 3, \"pai_nombre\": \"Argentina\", \"pai_codigo\": \"AR\" }\n  ]\n}"
            }
          ]
        },
        {
          "name": "Listar Departamentos por País",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/ubicaciones/paises/1/departamentos",
              "host": ["{{base_url}}"],
              "path": ["ubicaciones", "paises", "1", "departamentos"]
            },
            "description": "Retorna la lista de departamentos/estados de un país específico."
          },
          "response": [
            {
              "name": "200 Departamentos obtenidos",
              "status": "OK",
              "code": 200,
              "body": "{\n  \"success\": true,\n  \"message\": \"Listado de departamentos\",\n  \"data\": [\n    { \"dep_id\": 1, \"dep_nombre\": \"Antioquia\", \"pai_id\": 1 },\n    { \"dep_id\": 2, \"dep_nombre\": \"Cundinamarca\", \"pai_id\": 1 },\n    { \"dep_id\": 3, \"dep_nombre\": \"Valle del Cauca\", \"pai_id\": 1 }\n  ]\n}"
            },
            {
              "name": "400 País inválido",
              "status": "Bad Request",
              "code": 400,
              "body": "{\n  \"success\": false,\n  \"message\": \"Identificador de país inválido\"\n}"
            }
          ]
        },
        {
          "name": "Listar Ciudades por Departamento",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{base_url}}/ubicaciones/departamentos/1/ciudades",
              "host": ["{{base_url}}"],
              "path": ["ubicaciones", "departamentos", "1", "ciudades"]
            },
            "description": "Retorna la lista de ciudades/municipios de un departamento específico."
          },
          "response": [
            {
              "name": "200 Ciudades obtenidas",
              "status": "OK",
              "code": 200,
              "body": "{\n  \"success\": true,\n  \"message\": \"Listado de ciudades\",\n  \"data\": [\n    { \"ciu_id\": 1, \"ciu_nombre\": \"Medellín\", \"dep_id\": 1 },\n    { \"ciu_id\": 2, \"ciu_nombre\": \"Bello\", \"dep_id\": 1 },\n    { \"ciu_id\": 3, \"ciu_nombre\": \"Envigado\", \"dep_id\": 1 }\n  ]\n}"
            },
            {
              "name": "400 Departamento inválido",
              "status": "Bad Request",
              "code": 400,
              "body": "{\n  \"success\": false,\n  \"message\": \"Identificador de departamento inválido\"\n}"
            }
          ]
        }
      ]
    },
    {
      "name": "Proyectos",
      "description": "Módulo de proyectos: creación, gestión, postulaciones y búsqueda de proyectos compatibles.",
      "item": [
        {
          "name": "Crear Proyecto",
          "request": {
            "method": "POST",
            "header": [
              { "key": "Authorization", "value": "Bearer {{token}}" },
              { "key": "Content-Type", "value": "application/json" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"titulo\": \"App de Delivery con React Native\",\n  \"descripcion\": \"Desarrollo de una aplicación móvil de delivery con seguimiento en tiempo real.\",\n  \"ciudad_id\": 1,\n  \"remoto\": true,\n  \"inversion\": \"USD 50,000\",\n  \"imagen\": \"data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD...\",\n  \"perfiles\": [\n    {\n      \"ppf_id\": 1,\n      \"cupos\": 2,\n      \"habilidades_requeridas\": [5, 9]\n    },\n    {\n      \"ppf_id\": 2,\n      \"cupos\": 1,\n      \"habilidades_requeridas\": [1, 3]\n    }\n  ],\n  \"socios\": [\n    {\n      \"usu_id\": 7,\n      \"hab_id\": 5\n    }\n  ],\n  \"acciones\": [\n    \"Definir MVP y backlog inicial\",\n    \"Preparar pitch para inversores\"\n  ]\n}",
              "options": { "raw": { "language": "json" } }
            },
            "url": {
              "raw": "{{base_url}}/proyectos",
              "host": ["{{base_url}}"],
              "path": ["proyectos"]
            },
            "description": "Crea un nuevo proyecto. Requiere definir al menos un perfil profesional con habilidades requeridas y cupos. Opcionalmente se pueden registrar socios iniciales y enviar la portada codificada en base64 dentro del JSON (campo imagen). Para multipart/form-data también se acepta un archivo, pero el body por defecto permanece en modo raw."
          },
          "response": [
            {
              "name": "200 Proyecto creado",
              "status": "OK",
              "code": 200,
              "body": "{\n  \"success\": true,\n  \"message\": \"Proyecto creado exitosamente\",\n  \"data\": {\n    \"proyecto_id\": 10,\n    \"proyecto\": {\n      \"pro_id\": 10,\n      \"usu_id\": 1,\n      \"pro_titulo\": \"App de Delivery con React Native\",\n      \"pro_descripcion\": \"Desarrollo de una aplicación móvil...\",\n      \"pro_imagen\": \"uploads/proyectos/proyecto_abc123.jpg\",\n      \"pro_ciudad_id\": 1,\n      \"pro_remoto\": true,\n      \"pro_inversion\": \"USD 50,000\",\n      \"pro_estado\": 1,\n      \"pro_fecha_inicio\": \"2025-03-12 22:00:00\"\n    },\n    \"perfiles\": [\n      { \"ppf_id\": 1, \"prp_cupos\": 2, \"prp_cupos_ocupados\": 1, \"habilidades\": [] }\n    ],\n    \"socios\": [],\n    \"acciones\": [\n      { \"pro_acc_id\": 1, \"pro_acc_descripcion\": \"Definir MVP y backlog inicial\", \"pro_acc_estado\": 0 },\n      { \"pro_acc_id\": 2, \"pro_acc_descripcion\": \"Preparar pitch para inversores\", \"pro_acc_estado\": 0 }\n    ]\n  }\n}"
            },
            {
              "name": "400 Datos inválidos",
              "status": "Bad Request",
              "code": 400,
              "body": "{\n  \"success\": false,\n  \"message\": \"El título es requerido\"\n}"
            }
          ]
        },
        {
          "name": "Listar Mis Proyectos",
          "request": {
            "method": "GET",
            "header": [
              { "key": "Authorization", "value": "Bearer {{token}}" }
            ],
            "url": {
              "raw": "{{base_url}}/proyectos",
              "host": ["{{base_url}}"],
              "path": ["proyectos"]
            },
            "description": "Lista todos los proyectos creados por el usuario autenticado."
          },
          "response": [
            {
              "name": "200 Proyectos obtenidos",
              "status": "OK",
              "code": 200,
              "body": "{\n  \"success\": true,\n  \"message\": \"Proyectos obtenidos exitosamente\",\n  \"data\": [\n    {\n      \"pro_id\": 10,\n      \"pro_titulo\": \"App de Delivery\",\n      \"pro_descripcion\": \"...\",\n      \"pro_inversion\": \"USD 50,000\",\n      \"pro_remoto\": true,\n      \"pro_estado\": 1,\n      \"pro_fecha_inicio\": \"2025-03-12 22:00:00\",\n      \"acciones\": [\n        { \"pro_acc_id\": 1, \"pro_acc_descripcion\": \"Definir MVP y backlog inicial\", \"pro_acc_estado\": 0 }\n      ]\n    }\n  ]\n}"
            }
          ]
        },
        {
          "name": "Obtener Proyecto por ID",
          "request": {
            "method": "GET",
            "header": [
              { "key": "Authorization", "value": "Bearer {{token}}" }
            ],
            "url": {
              "raw": "{{base_url}}/proyectos/obtener?id=10",
              "host": ["{{base_url}}"],
              "path": ["proyectos", "obtener"],
              "query": [
                { "key": "id", "value": "10", "description": "ID del proyecto (requerido, numérico)" }
              ]
            },
            "description": "Obtiene el detalle de un proyecto específico del usuario autenticado por su ID."
          },
          "response": [
            {
              "name": "200 Proyecto obtenido",
              "status": "OK",
              "code": 200,
              "body": "{\n  \"success\": true,\n  \"message\": \"Proyecto obtenido exitosamente\",\n  \"data\": {\n    \"pro_id\": 10,\n    \"usu_id\": 1,\n    \"pro_titulo\": \"App de Delivery con React Native\",\n    \"pro_descripcion\": \"Desarrollo de una aplicación móvil...\",\n    \"pro_ciudad_id\": 1,\n    \"pro_remoto\": true,\n    \"pro_inversion\": \"USD 50,000\",\n    \"pro_estado\": 1,\n    \"pro_fecha_inicio\": \"2025-03-12 22:00:00\",\n    \"acciones\": [\n      { \"pro_acc_id\": 1, \"pro_acc_descripcion\": \"Definir MVP y backlog inicial\", \"pro_acc_estado\": 0 },\n      { \"pro_acc_id\": 2, \"pro_acc_descripcion\": \"Preparar pitch para inversores\", \"pro_acc_estado\": 0 }\n    ]\n  }\n}"
            },
            {
              "name": "404 Proyecto no encontrado",
              "status": "Not Found",
              "code": 404,
              "body": "{\n  \"success\": false,\n  \"message\": \"Proyecto no encontrado\"\n}"
            },
            {
              "name": "403 No autorizado",
              "status": "Forbidden",
              "code": 403,
              "body": "{\n  \"success\": false,\n  \"message\": \"No autorizado\"\n}"
            }
          ]
        },
        {
          "name": "Actualizar Proyecto",
          "request": {
            "method": "PUT",
            "header": [
              { "key": "Authorization", "value": "Bearer {{token}}" },
              { "key": "Content-Type", "value": "application/json" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"titulo\": \"App de Delivery v2 - React Native\",\n  \"descripcion\": \"Versión actualizada con nuevas funcionalidades de seguimiento.\",\n  \"ciudad_id\": 2,\n  \"remoto\": false,\n  \"inversion\": \"USD 65,000\",\n  \"fecha_inicio\": \"2025-04-01\",\n  \"eliminar_imagen\": false,\n  \"imagen\": null,\n  \"acciones\": [\n    \"Iterar sobre pruebas de usuario\",\n    \"Preparar lanzamiento en tiendas\"\n  ]\n}",
              "options": { "raw": { "language": "json" } }
            },
            "url": {
              "raw": "{{base_url}}/proyectos/actualizar?id=10",
              "host": ["{{base_url}}"],
              "path": ["proyectos", "actualizar"],
              "query": [
                { "key": "id", "value": "10", "description": "ID del proyecto a actualizar (requerido)" }
              ]
            },
            "description": "Actualiza los datos de un proyecto existente del usuario autenticado. El body permanece en modo raw JSON; se puede enviar `imagen` con base64 para reemplazar la portada o establecer `eliminar_imagen` en true para removerla. (Si se requiere multipart/form-data, cambiar manualmente el modo del request.)"
          },
          "response": [
            {
              "name": "200 Proyecto actualizado",
              "status": "OK",
              "code": 200,
              "body": "{\n  \"success\": true,\n  \"message\": \"Proyecto actualizado exitosamente\",\n  \"data\": {\n    \"proyecto_id\": 10,\n    \"proyecto\": { \n      \"pro_id\": 10, \n      \"pro_titulo\": \"App de Delivery v2 - React Native\",\n      \"pro_inversion\": \"USD 65,000\",\n      \"pro_imagen\": \"uploads/proyectos/proyecto_nuevo.jpg\"\n    },\n    \"acciones\": [\n      { \"pro_acc_id\": 1, \"pro_acc_descripcion\": \"Iterar sobre pruebas de usuario\", \"pro_acc_estado\": 0 },\n      { \"pro_acc_id\": 2, \"pro_acc_descripcion\": \"Preparar lanzamiento en tiendas\", \"pro_acc_estado\": 0 }\n    ],\n    \"mensaje\": \"Proyecto actualizado exitosamente\"\n  }\n}"
            },
            {
              "name": "403 No autorizado",
              "status": "Forbidden",
              "code": 403,
              "body": "{\n  \"success\": false,\n  \"message\": \"No autorizado\"\n}"
            }
          ]
        },
        {
          "name": "Eliminar (Anular) Proyecto",
          "request": {
            "method": "DELETE",
            "header": [
              { "key": "Authorization", "value": "Bearer {{token}}" }
            ],
            "url": {
              "raw": "{{base_url}}/proyectos/eliminar?id=10",
              "host": ["{{base_url}}"],
              "path": ["proyectos", "eliminar"],
              "query": [
                { "key": "id", "value": "10", "description": "ID del proyecto a anular (requerido)" }
              ]
            },
            "description": "Anula (soft-delete) un proyecto. El proyecto pasa a estado 0 (inactivo). Solo el propietario puede anularlo."
          },
          "response": [
            {
              "name": "200 Proyecto anulado",
              "status": "OK",
              "code": 200,
              "body": "{\n  \"success\": true,\n  \"message\": \"Proyecto anulado exitosamente\",\n  \"data\": {\n    \"proyecto_id\": 10,\n    \"estado\": 0,\n    \"mensaje\": \"Proyecto anulado exitosamente\"\n  }\n}"
            },
            {
              "name": "400 Proyecto ya anulado",
              "status": "Bad Request",
              "code": 400,
              "body": "{\n  \"success\": false,\n  \"message\": \"El proyecto ya está anulado\"\n}"
            }
          ]
        },
        {
          "name": "Postularse a Proyecto",
          "request": {
            "method": "POST",
            "header": [
              { "key": "Authorization", "value": "Bearer {{token}}" },
              { "key": "Content-Type", "value": "application/json" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"proyecto_id\": 10,\n  \"hab_id\": 5\n}",
              "options": { "raw": { "language": "json" } }
            },
            "url": {
              "raw": "{{base_url}}/proyectos/postular",
              "host": ["{{base_url}}"],
              "path": ["proyectos", "postular"]
            },
            "description": "Registra una postulación del usuario autenticado a un proyecto. Se requiere tener perfil profesional configurado y la habilidad debe pertenecer al perfil del usuario."
          },
          "response": [
            {
              "name": "200 Postulación registrada",
              "status": "OK",
              "code": 200,
              "body": "{\n  \"success\": true,\n  \"message\": \"Postulación registrada correctamente\",\n  \"data\": {\n    \"postulacion_id\": 15,\n    \"proyecto_id\": 10,\n    \"usuario_id\": 3,\n    \"hab_id\": 5,\n    \"cupos_disponibles\": 1\n  }\n}"
            },
            {
              "name": "400 Ya postulado",
              "status": "Bad Request",
              "code": 400,
              "body": "{\n  \"success\": false,\n  \"message\": \"Ya tienes una postulación en este proyecto\"\n}"
            },
            {
              "name": "400 Perfil incompleto",
              "status": "Bad Request",
              "code": 400,
              "body": "{\n  \"success\": false,\n  \"message\": \"Debes completar tu perfil profesional antes de postularte\"\n}"
            }
          ]
        },
        {
          "name": "Listar Postulaciones",
          "request": {
            "method": "GET",
            "header": [
              { "key": "Authorization", "value": "Bearer {{token}}" }
            ],
            "url": {
              "raw": "{{base_url}}/proyectos/postulaciones?proyecto_id=10&estado=1",
              "host": ["{{base_url}}"],
              "path": ["proyectos", "postulaciones"],
              "query": [
                { "key": "proyecto_id", "value": "10", "description": "Filtrar por proyecto específico (opcional)" },
                { "key": "estado", "value": "1", "description": "Filtrar por estado: 1=pendiente, 2=aceptada, 3=rechazada (opcional)" }
              ]
            },
            "description": "Lista las postulaciones recibidas en los proyectos del usuario autenticado (como propietario). Incluye datos del postulante."
          },
          "response": [
            {
              "name": "200 Postulaciones obtenidas",
              "status": "OK",
              "code": 200,
              "body": "{\n  \"success\": true,\n  \"message\": \"Postulaciones obtenidas correctamente\",\n  \"data\": [\n    {\n      \"postulacion\": {\n        \"ppo_id\": 15,\n        \"proyecto_id\": 10,\n        \"hab_id\": 5,\n        \"estado\": 1,\n        \"fecha\": \"2025-03-12 20:00:00\",\n        \"proyecto\": { \"pro_id\": 10, \"titulo\": \"App de Delivery\", \"estado\": 1 }\n      },\n      \"postulante\": {\n        \"usuario\": { \"usu_id\": 3, \"usu_email\": \"postulante@ejemplo.com\" },\n        \"perfil\": { \"per_nombres\": \"María\", \"per_ape_paterno\": \"García\" },\n        \"perfil_profesional\": { \"ppf_nombre\": \"Desarrollador Frontend\" },\n        \"habilidades\": [{ \"hab_id\": 5, \"hab_nombre\": \"JavaScript\" }]\n      }\n    }\n  ]\n}"
            }
          ]
        },
        {
          "name": "Gestionar Postulación",
          "request": {
            "method": "POST",
            "header": [
              { "key": "Authorization", "value": "Bearer {{token}}" },
              { "key": "Content-Type", "value": "application/json" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"postulacion_id\": 15,\n  \"accion\": \"aceptar\"\n}",
              "options": { "raw": { "language": "json" } }
            },
            "url": {
              "raw": "{{base_url}}/proyectos/postulaciones/gestionar",
              "host": ["{{base_url}}"],
              "path": ["proyectos", "postulaciones", "gestionar"]
            },
            "description": "Acepta o rechaza una postulación. Solo el propietario del proyecto puede gestionar postulaciones. Al aceptar: se crea el miembro, se reserva el cupo y se genera la conversación."
          },
          "response": [
            {
              "name": "200 Postulación aceptada",
              "status": "OK",
              "code": 200,
              "body": "{\n  \"success\": true,\n  \"message\": \"Postulación gestionada correctamente\",\n  \"data\": {\n    \"postulacion_id\": 15,\n    \"estado\": 2,\n    \"miembro\": { \"pro_id\": 10, \"usu_id\": 3, \"hab_id\": 5 },\n    \"conversacion_id\": 8,\n    \"cupos_disponibles\": 0\n  }\n}"
            },
            {
              "name": "200 Postulación rechazada",
              "status": "OK",
              "code": 200,
              "body": "{\n  \"success\": true,\n  \"message\": \"Postulación gestionada correctamente\",\n  \"data\": {\n    \"postulacion_id\": 15,\n    \"estado\": 3,\n    \"resultado\": \"rechazada\"\n  }\n}"
            },
            {
              "name": "400 Acción inválida",
              "status": "Bad Request",
              "code": 400,
              "body": "{\n  \"success\": false,\n  \"message\": \"Acción inválida, use \\\"aceptar\\\" o \\\"rechazar\\\"\"\n}"
            }
          ]
        },
        {
          "name": "Listar Proyectos Compatibles",
          "request": {
            "method": "GET",
            "header": [
              { "key": "Authorization", "value": "Bearer {{token}}" }
            ],
            "url": {
              "raw": "{{base_url}}/proyectos/compatibles?limite=10&offset=0&pagina=1",
              "host": ["{{base_url}}"],
              "path": ["proyectos", "compatibles"],
              "query": [
                { "key": "limite", "value": "10", "description": "Cantidad de resultados por página (opcional)" },
                { "key": "offset", "value": "0", "description": "Desplazamiento para paginación (opcional)" },
                { "key": "pagina", "value": "1", "description": "Número de página (opcional)" }
              ]
            },
            "description": "Lista proyectos compatibles con el perfil profesional y habilidades del usuario autenticado. Implementa un sistema de matching."
          },
          "response": [
            {
              "name": "200 Proyectos compatibles",
              "status": "OK",
              "code": 200,
              "body": "{\n  \"success\": true,\n  \"message\": \"Proyectos compatibles obtenidos correctamente\",\n  \"data\": [\n    {\n      \"pro_id\": 5,\n      \"pro_titulo\": \"E-commerce con Laravel\",\n      \"pro_descripcion\": \"Tienda online completa...\",\n      \"pro_remoto\": true,\n      \"cupos_disponibles\": 2,\n      \"compatibilidad\": 85\n    }\n  ]\n}"
            }
          ]
        }
      ]
    },
    {
      "name": "Chat",
      "description": "Módulo de mensajería entre miembros de proyectos. Gestiona conversaciones y mensajes de texto e imagen.",
      "item": [
        {
          "name": "Listar Conversaciones",
          "request": {
            "method": "GET",
            "header": [
              { "key": "Authorization", "value": "Bearer {{token}}" }
            ],
            "url": {
              "raw": "{{base_url}}/chat/conversaciones",
              "host": ["{{base_url}}"],
              "path": ["chat", "conversaciones"]
            },
            "description": "Obtiene todas las conversaciones del usuario autenticado e incluye el total. Si no existen conversaciones devuelve un mensaje contextual."
          },
          "response": [
            {
              "name": "200 Conversaciones obtenidas",
              "status": "OK",
              "code": 200,
              "body": "{\n  \"success\": true,\n  \"message\": \"Conversaciones obtenidas correctamente\",\n  \"data\": {\n    \"total\": 1,\n    \"conversaciones\": [\n      {\n        \"pc_id\": 8,\n        \"pro_id\": 10,\n        \"creador_id\": 1,\n        \"socio_id\": 3,\n        \"pc_estado\": 1,\n        \"proyecto\": { \"pro_titulo\": \"App de Delivery\" }\n      }\n    ]\n  }\n}"
            },
            {
              "name": "200 Sin conversaciones",
              "status": "OK",
              "code": 200,
              "body": "{\n  \"success\": true,\n  \"message\": \"Aún no tienes conversaciones disponibles\",\n  \"data\": {\n    \"total\": 0,\n    \"conversaciones\": []\n  }\n}"
            }
          ]
        },
        {
          "name": "Obtener Mensajes",
          "request": {
            "method": "GET",
            "header": [
              { "key": "Authorization", "value": "Bearer {{token}}" }
            ],
            "url": {
              "raw": "{{base_url}}/chat/mensajes?conversacion_id=8&limite=50&offset=0",
              "host": ["{{base_url}}"],
              "path": ["chat", "mensajes"],
              "query": [
                { "key": "conversacion_id", "value": "8", "description": "ID de la conversación (requerido)" },
                { "key": "limite", "value": "50", "description": "Cantidad de mensajes a retornar (opcional, default: 50)" },
                { "key": "offset", "value": "0", "description": "Desplazamiento para paginación (opcional, default: 0)" }
              ]
            },
            "description": "Obtiene los mensajes de una conversación específica con paginación. El usuario debe ser participante de la conversación."
          },
          "response": [
            {
              "name": "200 Mensajes obtenidos",
              "status": "OK",
              "code": 200,
              "body": "{\n  \"success\": true,\n  \"message\": \"Mensajes obtenidos correctamente\",\n  \"data\": {\n    \"conversacion_id\": 8,\n    \"mensajes\": [\n      {\n        \"pm_id\": 1,\n        \"pc_id\": 8,\n        \"usu_id\": 1,\n        \"pm_tipo\": \"texto\",\n        \"pm_contenido\": \"Hola, bienvenido al proyecto!\",\n        \"pm_fecha\": \"2025-03-12 22:30:00\"\n      }\n    ]\n  }\n}"
            },
            {
              "name": "400 conversacion_id requerido",
              "status": "Bad Request",
              "code": 400,
              "body": "{\n  \"success\": false,\n  \"message\": \"Debe indicar un conversacion_id válido\"\n}"
            }
          ]
        },
        {
          "name": "Enviar Mensaje de Texto",
          "request": {
            "method": "POST",
            "header": [
              { "key": "Authorization", "value": "Bearer {{token}}" },
              { "key": "Content-Type", "value": "application/json" }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"conversacion_id\": 8,\n  \"contenido\": \"¡Hola! ¿Cómo avanza el proyecto?\"\n}",
              "options": { "raw": { "language": "json" } }
            },
            "url": {
              "raw": "{{base_url}}/chat/mensajes/texto",
              "host": ["{{base_url}}"],
              "path": ["chat", "mensajes", "texto"]
            },
            "description": "Envía un mensaje de texto en una conversación. El usuario debe ser participante de la conversación."
          },
          "response": [
            {
              "name": "200 Mensaje enviado",
              "status": "OK",
              "code": 200,
              "body": "{\n  \"success\": true,\n  \"message\": \"Mensaje enviado correctamente\",\n  \"data\": {\n    \"pm_id\": 25,\n    \"pc_id\": 8,\n    \"usu_id\": 1,\n    \"pm_tipo\": \"texto\",\n    \"pm_contenido\": \"¡Hola! ¿Cómo avanza el proyecto?\",\n    \"pm_fecha\": \"2025-03-12 22:45:00\"\n  }\n}"
            },
            {
              "name": "400 Datos requeridos",
              "status": "Bad Request",
              "code": 400,
              "body": "{\n  \"success\": false,\n  \"message\": \"Debe enviar conversacion_id y contenido\"\n}"
            }
          ]
        },
        {
          "name": "Enviar Mensaje de Imagen",
          "request": {
            "method": "POST",
            "header": [
              { "key": "Authorization", "value": "Bearer {{token}}" }
            ],
            "body": {
              "mode": "formdata",
              "formdata": [
                {
                  "key": "conversacion_id",
                  "value": "8",
                  "type": "text",
                  "description": "ID de la conversación (requerido)"
                },
                {
                  "key": "archivo",
                  "type": "file",
                  "src": "",
                  "description": "Archivo de imagen a enviar (requerido): JPG, PNG, GIF, etc."
                }
              ]
            },
            "url": {
              "raw": "{{base_url}}/chat/mensajes/imagen",
              "host": ["{{base_url}}"],
              "path": ["chat", "mensajes", "imagen"]
            },
            "description": "Envía un mensaje con imagen en una conversación. Usar multipart/form-data. El campo 'archivo' debe ser el archivo de imagen."
          },
          "response": [
            {
              "name": "200 Imagen enviada",
              "status": "OK",
              "code": 200,
              "body": "{\n  \"success\": true,\n  \"message\": \"Imagen enviada correctamente\",\n  \"data\": {\n    \"pm_id\": 26,\n    \"pc_id\": 8,\n    \"usu_id\": 1,\n    \"pm_tipo\": \"imagen\",\n    \"adjunto\": {\n      \"url\": \"https://storage.ejemplo.com/chat/img_abc123.jpg\",\n      \"nombre\": \"screenshot.jpg\"\n    },\n    \"pm_fecha\": \"2025-03-12 22:50:00\"\n  }\n}"
            },
            {
              "name": "400 Archivo requerido",
              "status": "Bad Request",
              "code": 400,
              "body": "{\n  \"success\": false,\n  \"message\": \"Debe enviar conversacion_id y el archivo de imagen\"\n}"
            }
          ]
        }
      ]
    }
  ]
}
