Initial commit
This commit is contained in:
547
docs/api-contract.md
Normal file
547
docs/api-contract.md
Normal file
@@ -0,0 +1,547 @@
|
||||
# API Contract - ServiceManagerWeb
|
||||
# Mesa de Ayuda B2B - Especificación de Endpoints
|
||||
|
||||
## Base URL
|
||||
- Desarrollo: `http://localhost:8000`
|
||||
- Producción: `https://api.servicemanager.aduanasoft.com`
|
||||
|
||||
## Versionado
|
||||
- Todos los endpoints tienen prefijo `/v1/`
|
||||
- Versionado en URL path (no headers)
|
||||
|
||||
## Autenticación
|
||||
- JWT Bearer Token en header `Authorization: Bearer <token>`
|
||||
- Refresh token para renovación automática
|
||||
|
||||
## Headers Estándar
|
||||
```
|
||||
Authorization: Bearer <jwt_token>
|
||||
Content-Type: application/json
|
||||
X-Tenant-ID: <tenant_uuid> # Requerido para endpoints multi-tenant
|
||||
X-Correlation-ID: <uuid> # Opcional para tracking
|
||||
Accept-Language: es-ES # Para internacionalización
|
||||
```
|
||||
|
||||
## Responses Estándar
|
||||
|
||||
### Éxito (2xx)
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"data": { ... },
|
||||
"message": "Operación exitosa",
|
||||
"metadata": {
|
||||
"page": 1,
|
||||
"per_page": 20,
|
||||
"total": 150,
|
||||
"total_pages": 8
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Error (4xx/5xx)
|
||||
```json
|
||||
{
|
||||
"success": false,
|
||||
"error": {
|
||||
"code": "VALIDATION_ERROR",
|
||||
"message": "Datos inválidos",
|
||||
"details": [
|
||||
{
|
||||
"field": "email",
|
||||
"message": "Email inválido"
|
||||
}
|
||||
]
|
||||
},
|
||||
"correlation_id": "uuid"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## DOMINIO: AUTH
|
||||
|
||||
### POST /v1/auth/login
|
||||
**Descripción**: Autenticación de usuario
|
||||
**Público**: Sí
|
||||
|
||||
**Request Body**:
|
||||
```json
|
||||
{
|
||||
"email": "user@example.com",
|
||||
"password": "password123",
|
||||
"tenant_slug": "aduanasoft-demo",
|
||||
"totp_code": "123456" // opcional, solo si 2FA activado
|
||||
}
|
||||
```
|
||||
|
||||
**Response 200**:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"access_token": "jwt_token",
|
||||
"refresh_token": "refresh_token",
|
||||
"expires_in": 3600,
|
||||
"user": {
|
||||
"id": "uuid",
|
||||
"email": "user@example.com",
|
||||
"first_name": "Juan",
|
||||
"last_name": "Pérez",
|
||||
"role": "AGENT",
|
||||
"tenant": {
|
||||
"id": "uuid",
|
||||
"name": "Aduanasoft Demo",
|
||||
"slug": "aduanasoft-demo"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### POST /v1/auth/refresh
|
||||
**Descripción**: Renovar access token
|
||||
**Público**: Sí
|
||||
|
||||
**Request Body**:
|
||||
```json
|
||||
{
|
||||
"refresh_token": "refresh_token"
|
||||
}
|
||||
```
|
||||
|
||||
### POST /v1/auth/logout
|
||||
**Descripción**: Cerrar sesión (revoca refresh token)
|
||||
**Autenticado**: Sí
|
||||
|
||||
### GET /v1/auth/me
|
||||
**Descripción**: Información del usuario actual
|
||||
**Autenticado**: Sí
|
||||
|
||||
### PUT /v1/auth/me
|
||||
**Descripción**: Actualizar perfil propio
|
||||
**Autenticado**: Sí
|
||||
|
||||
**Request Body**:
|
||||
```json
|
||||
{
|
||||
"first_name": "Juan",
|
||||
"last_name": "Pérez",
|
||||
"language": "es",
|
||||
"timezone": "America/Mexico_City",
|
||||
"notifications_email": true
|
||||
}
|
||||
```
|
||||
|
||||
### POST /v1/auth/change-password
|
||||
**Descripción**: Cambiar contraseña
|
||||
**Autenticado**: Sí
|
||||
|
||||
### POST /v1/auth/2fa/setup
|
||||
**Descripción**: Configurar 2FA (solo roles internos)
|
||||
**Autenticado**: Sí, Roles: ADMIN, SUPPORT_MANAGER, AGENT, AUDITOR
|
||||
|
||||
### POST /v1/auth/2fa/verify
|
||||
**Descripción**: Verificar código 2FA durante setup
|
||||
**Autenticado**: Sí
|
||||
|
||||
---
|
||||
|
||||
## DOMINIO: TENANTS
|
||||
|
||||
### GET /v1/tenants/current
|
||||
**Descripción**: Información del tenant actual
|
||||
**Autenticado**: Sí
|
||||
|
||||
### PUT /v1/tenants/current
|
||||
**Descripción**: Actualizar tenant (solo CLIENT_ADMIN/ADMIN)
|
||||
**Autenticado**: Sí, Roles: CLIENT_ADMIN, ADMIN
|
||||
|
||||
### GET /v1/tenants (solo ADMIN)
|
||||
**Descripción**: Listar todos los tenants
|
||||
**Autenticado**: Sí, Roles: ADMIN
|
||||
|
||||
### POST /v1/tenants (solo ADMIN)
|
||||
**Descripción**: Crear nuevo tenant
|
||||
**Autenticado**: Sí, Roles: ADMIN
|
||||
|
||||
---
|
||||
|
||||
## DOMINIO: USERS
|
||||
|
||||
### GET /v1/users
|
||||
**Descripción**: Listar usuarios del tenant
|
||||
**Autenticado**: Sí
|
||||
**Roles**: Todos (filtros por rol)
|
||||
|
||||
**Query Params**:
|
||||
```
|
||||
?page=1&per_page=20&role=AGENT&is_active=true&search=juan
|
||||
```
|
||||
|
||||
**Response 200**:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"data": [
|
||||
{
|
||||
"id": "uuid",
|
||||
"email": "agent@example.com",
|
||||
"first_name": "Juan",
|
||||
"last_name": "Agente",
|
||||
"role": "AGENT",
|
||||
"is_active": true,
|
||||
"email_verified": true,
|
||||
"last_login": "2024-01-15T10:30:00Z",
|
||||
"created_at": "2024-01-01T00:00:00Z"
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"page": 1,
|
||||
"per_page": 20,
|
||||
"total": 150,
|
||||
"total_pages": 8
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### POST /v1/users
|
||||
**Descripción**: Crear usuario
|
||||
**Autenticado**: Sí, Roles: ADMIN, SUPPORT_MANAGER, CLIENT_ADMIN
|
||||
|
||||
**Request Body**:
|
||||
```json
|
||||
{
|
||||
"email": "nuevo@example.com",
|
||||
"first_name": "Nuevo",
|
||||
"last_name": "Usuario",
|
||||
"role": "AGENT",
|
||||
"password": "temporal123", // opcional, se genera automáticamente
|
||||
"send_welcome_email": true
|
||||
}
|
||||
```
|
||||
|
||||
### GET /v1/users/{user_id}
|
||||
**Descripción**: Obtener usuario específico
|
||||
**Autenticado**: Sí
|
||||
|
||||
### PUT /v1/users/{user_id}
|
||||
**Descripción**: Actualizar usuario
|
||||
**Autenticado**: Sí, Roles: ADMIN, SUPPORT_MANAGER, CLIENT_ADMIN
|
||||
|
||||
### DELETE /v1/users/{user_id}
|
||||
**Descripción**: Desactivar usuario (soft delete)
|
||||
**Autenticado**: Sí, Roles: ADMIN, SUPPORT_MANAGER, CLIENT_ADMIN
|
||||
|
||||
---
|
||||
|
||||
## DOMINIO: TICKETS
|
||||
|
||||
### GET /v1/tickets
|
||||
**Descripción**: Listar tickets con filtros
|
||||
**Autenticado**: Sí
|
||||
|
||||
**Query Params**:
|
||||
```
|
||||
?page=1&per_page=20
|
||||
&status=NEW,IN_PROGRESS
|
||||
&priority=HIGH,URGENT
|
||||
&assigned_to=uuid
|
||||
&created_by=uuid
|
||||
&category_id=uuid
|
||||
&search=problema+conexion
|
||||
&sort=created_at_desc
|
||||
&date_from=2024-01-01
|
||||
&date_to=2024-01-31
|
||||
```
|
||||
|
||||
**Response 200**:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"data": [
|
||||
{
|
||||
"id": "uuid",
|
||||
"ticket_number": "TKT-2024-000001",
|
||||
"subject": "Problema de conexión",
|
||||
"status": "IN_PROGRESS",
|
||||
"priority": "HIGH",
|
||||
"category": {
|
||||
"id": "uuid",
|
||||
"name": "Soporte Técnico"
|
||||
},
|
||||
"created_by": {
|
||||
"id": "uuid",
|
||||
"first_name": "Juan",
|
||||
"last_name": "Cliente"
|
||||
},
|
||||
"assigned_to": {
|
||||
"id": "uuid",
|
||||
"first_name": "Ana",
|
||||
"last_name": "Soporte"
|
||||
},
|
||||
"sla_response_due": "2024-01-15T12:00:00Z",
|
||||
"sla_resolution_due": "2024-01-16T10:00:00Z",
|
||||
"created_at": "2024-01-15T10:00:00Z",
|
||||
"updated_at": "2024-01-15T11:30:00Z"
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"page": 1,
|
||||
"per_page": 20,
|
||||
"total": 150,
|
||||
"total_pages": 8
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### POST /v1/tickets
|
||||
**Descripción**: Crear nuevo ticket
|
||||
**Autenticado**: Sí
|
||||
|
||||
**Request Body**:
|
||||
```json
|
||||
{
|
||||
"subject": "Problema de conexión con el sistema",
|
||||
"description": "Descripción detallada del problema...",
|
||||
"priority": "HIGH",
|
||||
"category_id": "uuid",
|
||||
"affected_system_id": "uuid",
|
||||
"attachments": [
|
||||
{
|
||||
"filename": "screenshot.png",
|
||||
"content_type": "image/png",
|
||||
"content_base64": "base64_data"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**Response 201**:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"id": "uuid",
|
||||
"ticket_number": "TKT-2024-000001",
|
||||
"subject": "Problema de conexión con el sistema",
|
||||
"status": "NEW",
|
||||
"sla_response_due": "2024-01-15T12:00:00Z",
|
||||
"created_at": "2024-01-15T10:00:00Z"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### GET /v1/tickets/{ticket_id}
|
||||
**Descripción**: Obtener ticket completo con comentarios
|
||||
**Autenticado**: Sí
|
||||
|
||||
**Response 200**:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"id": "uuid",
|
||||
"ticket_number": "TKT-2024-000001",
|
||||
"subject": "Problema de conexión",
|
||||
"description": "Descripción completa...",
|
||||
"status": "IN_PROGRESS",
|
||||
"priority": "HIGH",
|
||||
"category": {...},
|
||||
"affected_system": {...},
|
||||
"created_by": {...},
|
||||
"assigned_to": {...},
|
||||
"attachments": [...],
|
||||
"comments": [
|
||||
{
|
||||
"id": "uuid",
|
||||
"content": "Comentario del ticket...",
|
||||
"author": {...},
|
||||
"is_internal": false,
|
||||
"attachments": [...],
|
||||
"created_at": "2024-01-15T11:00:00Z"
|
||||
}
|
||||
],
|
||||
"status_history": [...],
|
||||
"sla_metrics": {
|
||||
"response_due": "2024-01-15T12:00:00Z",
|
||||
"resolution_due": "2024-01-16T10:00:00Z",
|
||||
"first_response_at": "2024-01-15T11:15:00Z",
|
||||
"response_sla_met": true,
|
||||
"resolution_sla_met": null
|
||||
},
|
||||
"created_at": "2024-01-15T10:00:00Z",
|
||||
"updated_at": "2024-01-15T11:30:00Z"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### PUT /v1/tickets/{ticket_id}
|
||||
**Descripción**: Actualizar ticket (estado, asignación, etc.)
|
||||
**Autenticado**: Sí
|
||||
|
||||
**Request Body**:
|
||||
```json
|
||||
{
|
||||
"status": "IN_PROGRESS",
|
||||
"assigned_to": "uuid",
|
||||
"priority": "URGENT",
|
||||
"comment": "Escalando por alta prioridad"
|
||||
}
|
||||
```
|
||||
|
||||
### POST /v1/tickets/{ticket_id}/comments
|
||||
**Descripción**: Agregar comentario al ticket
|
||||
**Autenticado**: Sí
|
||||
|
||||
**Request Body**:
|
||||
```json
|
||||
{
|
||||
"content": "Comentario con solución propuesta...",
|
||||
"is_internal": false,
|
||||
"attachments": [
|
||||
{
|
||||
"filename": "solution.pdf",
|
||||
"content_type": "application/pdf",
|
||||
"content_base64": "base64_data"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### PUT /v1/tickets/{ticket_id}/rating
|
||||
**Descripción**: Calificar ticket resuelto (solo cliente)
|
||||
**Autenticado**: Sí, Roles: CLIENT_ADMIN, CLIENT_USER
|
||||
|
||||
**Request Body**:
|
||||
```json
|
||||
{
|
||||
"rating": 5,
|
||||
"comment": "Excelente atención y resolución rápida"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## DOMINIO: CATEGORIES & SYSTEMS
|
||||
|
||||
### GET /v1/categories
|
||||
**Descripción**: Listar categorías del tenant
|
||||
**Autenticado**: Sí
|
||||
|
||||
### POST /v1/categories (solo staff interno)
|
||||
**Descripción**: Crear categoría
|
||||
**Autenticado**: Sí, Roles: ADMIN, SUPPORT_MANAGER
|
||||
|
||||
### GET /v1/affected-systems
|
||||
**Descripción**: Listar sistemas afectados
|
||||
**Autenticado**: Sí
|
||||
|
||||
---
|
||||
|
||||
## DOMINIO: NOTIFICATIONS
|
||||
|
||||
### GET /v1/notifications/templates (solo staff interno)
|
||||
**Descripción**: Listar templates de email
|
||||
**Autenticado**: Sí, Roles: ADMIN, SUPPORT_MANAGER
|
||||
|
||||
### POST /v1/notifications/test-email (solo ADMIN)
|
||||
**Descripción**: Enviar email de prueba
|
||||
**Autenticado**: Sí, Roles: ADMIN
|
||||
|
||||
---
|
||||
|
||||
## DOMINIO: REPORTS & ANALYTICS
|
||||
|
||||
### GET /v1/reports/dashboard
|
||||
**Descripción**: Métricas del dashboard
|
||||
**Autenticado**: Sí
|
||||
|
||||
**Response 200**:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"tickets": {
|
||||
"total": 150,
|
||||
"new": 12,
|
||||
"in_progress": 45,
|
||||
"waiting_customer": 8,
|
||||
"resolved_today": 15
|
||||
},
|
||||
"sla": {
|
||||
"response_rate": 95.5,
|
||||
"resolution_rate": 87.2
|
||||
},
|
||||
"agents": {
|
||||
"active": 8,
|
||||
"avg_load": 5.6
|
||||
},
|
||||
"csat": {
|
||||
"average": 4.2,
|
||||
"total_responses": 89
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### GET /v1/reports/tickets
|
||||
**Descripción**: Reporte de tickets con filtros
|
||||
**Autenticado**: Sí
|
||||
|
||||
---
|
||||
|
||||
## DOMINIO: AUDIT
|
||||
|
||||
### GET /v1/audit/logs (solo AUDITOR/ADMIN)
|
||||
**Descripción**: Consultar logs de auditoría
|
||||
**Autenticado**: Sí, Roles: AUDITOR, ADMIN
|
||||
|
||||
**Query Params**:
|
||||
```
|
||||
?page=1&per_page=50
|
||||
&action=ticket.create,ticket.assign
|
||||
&user_id=uuid
|
||||
&resource_type=ticket
|
||||
&date_from=2024-01-01
|
||||
&date_to=2024-01-31
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## CÓDIGOS DE ERROR ESTÁNDAR
|
||||
|
||||
- `400` - Bad Request (datos inválidos)
|
||||
- `401` - Unauthorized (no autenticado)
|
||||
- `403` - Forbidden (sin permisos)
|
||||
- `404` - Not Found (recurso no encontrado)
|
||||
- `409` - Conflict (recurso duplicado)
|
||||
- `422` - Unprocessable Entity (validación fallida)
|
||||
- `429` - Too Many Requests (rate limit)
|
||||
- `500` - Internal Server Error
|
||||
|
||||
## RATE LIMITING
|
||||
|
||||
- Auth endpoints: 10 req/min por IP
|
||||
- API endpoints: 100 req/min por usuario
|
||||
- File uploads: 5 req/min por usuario
|
||||
|
||||
## PAGINACIÓN
|
||||
|
||||
- Default: `per_page=20`, `max=100`
|
||||
- Links de navegación en metadata
|
||||
- Total count incluido cuando sea eficiente
|
||||
|
||||
## ORDENAMIENTO
|
||||
|
||||
Formato: `?sort=field_direction`
|
||||
- `created_at_desc` (default)
|
||||
- `updated_at_desc`
|
||||
- `priority_desc`
|
||||
- `status_asc`
|
||||
|
||||
## BÚSQUEDA
|
||||
|
||||
- Full-text search en `subject` y `description`
|
||||
- Búsqueda por número de ticket exacto
|
||||
- Filtros combinables con AND lógico
|
||||
Reference in New Issue
Block a user