Files
cotizador/api/COTIZADOR_API_SKILL.md
urieljarethandClaude Opus 5 20f535d485 Retirar el servidor MCP y fijar todas las dependencias del API
MCP: se retira por completo (endpoint, paquete api/app/mcp/ y dependencia).
Tres razones, en orden de peso:

- Nadie lo usa.
- Llevaba roto desde antes de este trabajo. Con credencial valida devolvia 500:
  el handler construia un StreamableHTTPServerTransport nuevo por peticion, sin
  manejo de sesion. Lo que estaba expuesto a internet era la puerta abierta de un
  cuarto averiado.
- Su SDK sin fijar tumbo el API entero en produccion al saltar a 2.0.0.

Retirarlo es una mitigacion mas fuerte que autenticarlo, que fue lo que hizo el
commit anterior. Recuperable con `git show edb500f5:api/app/mcp/server.py`.
Se limpia tambien la ruta "/mcp" que el endpoint raiz seguia anunciando, y las
menciones a MCP del docstring y la descripcion de Swagger.

Dependencias: once de las doce eran rangos `>=` sin techo, o sea que cada
reconstruccion era una tirada de dados contra PyPI. `pydantic>=2.0` habria
aceptado pydantic 3 con la misma alegria con la que `mcp>=1.0.0` acepto 2.0.0.
Ahora todas van fijadas a la version exacta que corre sana en produccion,
capturada con pip freeze del contenedor healthy.

El lado Next.js ya era reproducible via package-lock.json; por eso el web nunca
se cayo durante el incidente y el api si.

Docs actualizados: AGENTS.md, README.md, api/COTIZADOR_API_SKILL.md y el spec,
que ademas registra en su seccion 0 las tres desviaciones de Fase 0 respecto a
lo disenado (el Despliegue B cancelado, la retirada del MCP y la deriva de
dependencia).

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
2026-07-28 17:51:21 -06:00

458 lines
12 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Cotizador E3 — API Documentation for AI Skills
## Overview
REST API for Consultoría E3's digital marketing quotation system. Allows AI agents to manage clients, browse service catalogs, create quotations, calculate financing, generate PDF/Excel documents, and manage the full sales pipeline.
**Base URL:** `http://localhost:8000`
**Auth:** API Key via `X-API-Key` header or `Authorization: Bearer <key>`
**Content-Type:** `application/json`
**OpenAPI Spec:** `GET /openapi.json` (no auth required)
---
## Domain Context
Consultoría E3 is a digital marketing agency in Querétaro, México. Services are organized in 4 project phases:
- **Fase 0 — Auditoría / Acompañamiento:** Initial assessments and ongoing consulting
- **Fase 1 — Setup e Infraestructura:** One-time setup (websites, branding, CRM config)
- **Fase 2 — Publicidad y Manejo:** Ongoing advertising and social media management
- **Fase 3 — Contenido y SEO:** Content creation and search engine optimization
Two payment types:
- **unico:** One-time payment
- **mensual:** Monthly recurring payment
Currency: MXN (Mexican Pesos) or USD. IVA (tax) rate: 16%.
---
## Authentication
### API Key (for agents)
```
X-API-Key: your-api-key-here
```
or
```
Authorization: Bearer your-api-key-here
```
### JWT (for human login)
```
POST /auth/login
Body: { "email": "[email protected]", "password": "secret" }
Response: { "user": { "id", "name", "email", "role" } }
Sets cookie: cotizador-session (httpOnly, 7 days)
```
---
## Endpoints Reference
### Health & Info
#### GET /health
Health check endpoint. No auth required.
- **Response:** `{ "status": "ok", "timestamp": "2026-05-03T..." }`
#### GET /api-info
API capabilities and version info.
- **Response:** `{ "name": "Cotizador E3 API", "version": "1.0.0", "capabilities": [...] }`
---
### Clients (Clientes)
#### GET /clientes
List clients with optional search and pagination.
- **Query params:** `?q=texto&page=1&limit=50`
- **Response:** `{ "data": [Cliente], "meta": { "total", "page", "limit", "pages" } }`
#### POST /clientes
Create a new client.
- **Body:**
```json
{
"nombre": "Juan Pérez",
"empresa": "ACME Corp",
"email": "[email protected]",
"telefono": "4421234567"
}
```
- **Response (201):** Full Cliente object with `id`
#### GET /clientes/{id}
Get client details including recent quotations.
- **Response:** Cliente object + `cotizaciones` array
#### PUT /clientes/{id}
Update client information.
- **Body:** Same as POST (all fields optional for update)
#### DELETE /clientes/{id}
Delete client. Returns 409 if client has quotations.
- **Response (200):** `{ "ok": true }`
- **Error (409):** `{ "error": "Cliente tiene N cotizaciones asociadas" }`
---
### Service Catalog (Catálogo)
#### GET /catalogo
List all active services with optional filters.
- **Query params:** `?fase=0&tipoPago=mensual&categoriaId=xxx&q=seo`
- **Response:** Array of ServicioCatalogo objects
Each service:
```json
{
"id": "cuid",
"nombre": "Posicionamiento SEO",
"descripcion": "...",
"fase": 3,
"tipoPago": "mensual",
"precioBase": 2900,
"tiempoEntrega": "7 - 14 dias",
"entregablesDefault": ["Keyword research", "On-page optimization"],
"categoriaId": "cuid",
"variante": null,
"activo": true,
"orden": 0
}
```
#### POST /catalogo
Create a new service.
- **Body:**
```json
{
"nombre": "Nuevo Servicio",
"descripcion": "Descripción del servicio",
"fase": 1,
"tipoPago": "unico",
"precioBase": 5000,
"tiempoEntrega": "7 - 14 dias",
"entregablesDefault": ["Entregable 1", "Entregable 2"],
"categoriaId": "cuid",
"variante": null,
"orden": 0
}
```
#### GET /catalogo/{id}
Get service details with category info.
#### PUT /catalogo/{id}
Update service. Same body as POST.
#### DELETE /catalogo/{id}
Soft-delete if used in quotations, hard-delete otherwise.
- **Response:** `{ "ok": true, "archived": true|false }`
---
### Categories (Categorías)
Available: SEO (#10b981), Marketing (#6366f1), Paid Media (#f59e0b), Desarrollo Web (#3b82f6), Automatizaciones (#8b5cf6), CRM (#ec4899), Desarrollo Personalizado (#14b8a6)
#### GET /categorias
List all categories ordered by `orden`.
#### POST /categorias
- **Body:** `{ "nombre": "Nueva Cat", "descripcion": "...", "color": "#6b7280", "orden": 0 }`
#### GET /categorias/{id}
Get category with its services.
#### PUT /categorias/{id}
- **Body:** `{ "nombre", "descripcion", "color", "orden", "activo" }`
#### DELETE /categorias/{id}
Returns 409 if category has associated services.
---
### Quotations (Cotizaciones)
#### GET /cotizaciones
List quotations with filters.
- **Query params:** `?estado=borrador&asesorId=xxx&clienteId=xxx&q=texto&desde=2026-01-01&hasta=2026-12-31&page=1&limit=50`
- **Response:** Array with full relations (cliente, asesor, servicios+catalogo, planBucefalo)
#### POST /cotizaciones
Create a complete quotation. Auto-creates client if not found.
- **Body:**
```json
{
"numero": "UJ2605AG001",
"fecha": "2026-05-03",
"vigencia": "2026-05-24",
"moneda": "MXN",
"tipoCambio": "NA",
"proyecto": "MKT Digital",
"esquemaPago": "Pago Unico/Mensual",
"incluirBonos": false,
"incluirFinanciamiento": false,
"observaciones": "",
"asesorId": "cuid",
"cliente": {
"nombre": "Juan Pérez",
"empresa": "ACME Corp",
"email": "[email protected]",
"telefono": "4421234567"
},
"servicios": [
{
"catalogoId": "cuid",
"nombre": "SEO On-Page",
"fase": 3,
"tipoPago": "mensual",
"precio": 2900,
"tiempoEntrega": "7 - 14 dias",
"entregables": ["Keyword research", "On-page SEO"]
}
],
"planBucefalo": {
"nivel": "basico",
"precio": 1000
}
}
```
#### GET /cotizaciones/{id}
Get quotation with all relations.
#### PUT /cotizaciones/{id}
Update quotation. Replaces all services in a transaction.
- **Body:** Same as POST without `numero` and `asesorId`. Optional `estado`.
#### DELETE /cotizaciones/{id}
Hard-delete with cascade.
#### PATCH /cotizaciones/{id}/precio
Update price of a single service.
- **Body:** `{ "servicioId": "cuid", "precio": 7500 }`
#### PATCH /cotizaciones/{id}/estado
Change quotation status.
- **Body:** `{ "estado": "enviada" }``borrador`, `enviada`, `aprobada`, `rechazada`
#### POST /cotizaciones/{id}/duplicate
Duplicate quotation as new copy in `borrador` state.
---
### Packages (Paquetes)
#### GET /paquetes
List active packages with nested phases and services.
#### POST /paquetes
- **Body:**
```json
{
"nombre": "Paquete Básico",
"descripcion": "...",
"fases": [
{ "nombre": "Auditoría", "orden": 0 },
{ "nombre": "Setup", "orden": 1 }
]
}
```
#### GET /paquetes/{id}
Get package with full nested structure.
#### PUT /paquetes/{id}
- **Body:** `{ "nombre", "descripcion", "activo" }`
#### DELETE /paquetes/{id}
Hard-delete with cascade.
#### POST /paquetes/{id}/manage
Multi-action endpoint:
- `{ "action": "addFase", "nombre": "Fase 3", "orden": 2 }`
- `{ "action": "updateFase", "faseId": "cuid", "nombre": "New Name", "orden": 1 }`
- `{ "action": "deleteFase", "faseId": "cuid" }`
- `{ "action": "addServicio", "servicioCatalogoId": "cuid", "fasePaqueteId": "cuid" }`
- `{ "action": "removeServicio", "servicioCatalogoId": "cuid", "fasePaqueteId": "cuid" }`
---
### Configuration (Configuración)
Allowed keys: `color_primario`, `color_secundario`, `logo_base64`, `razon_social`, `rfc`, `domicilio_fiscal`, `cuenta_nacional`, `clabe_interbancaria`, `cuenta_internacional`, `cuenta_internacional_swift`, `hora_centinela`, `anualidad_hosting`, `iva`, `terminos_condiciones`, `no_incluye`, `notas_adicionales`
#### GET /configuracion
- **Response:** `{ "color_primario": "#2563eb", "razon_social": "...", ... }`
#### PUT /configuracion
Bulk upsert. Only whitelisted keys processed.
- **Body:** `{ "color_primario": "#ff0000", "razon_social": "Nuevo Nombre" }`
---
### Bonuses (Bonos)
Available: Centinela Web, Workshop Buyer Persona, Workshop Propuesta de Valor, Membresía Premium, Mes Gratis CRM, Script de Ventas
#### GET /bonos
---
### Financing (Financiamiento)
| Months | Rate | Commission | Min Amount |
|--------|------|------------|------------|
| 3 | 7.7% | 2.5% | $300 |
| 6 | 10.7% | 2.5% | $600 |
| 9 | 13.7% | 2.5% | $900 |
| 12 | 16.7% | 2.5% | $1,200 |
#### GET /financiamiento/planes
#### POST /financiamiento/calcular
- **Body:** `{ "monto": 50000, "meses": 6 }`
- **Response:** `{ "pagoMensual", "ivaMensual", "totalMensual", "comisionTotal", "granTotal" }`
#### GET /financiamiento/simulacion/{cotizacion_id}
Auto-calculate financing for existing quotation.
---
### Export
#### POST /export/pdf — PDF from draft data
#### GET /export/pdf/{id} — PDF from saved quotation
#### POST /export/excel — Excel from draft data
#### GET /export/excel/{id} — Excel from saved quotation
#### GET /export/catalogo — CSV of active services
#### GET /export/catalogo/plantilla — CSV import template
---
### Import
#### POST /import/catalogo
Bulk import services from CSV. `multipart/form-data` with `file` field.
- **Response:** `{ "creados": 5, "omitidos": 2, "errores": [...] }`
---
## Business Rules
### Quotation Number
Format: `UJ{YY}{MM}{AsesorInitials}{sequence}` — e.g., `UJ2605AG001`
### Quotation Validity
15 business days from issue (excludes Sat/Sun).
### CRM Bucefalo Plans
basico=$1,000/mes, estandar=$3,500/mes, premium=$4,500/mes, empresarial=$7,500/mes
### Client Deduplication
Matched by `(nombre, empresa)` pair.
### Service Soft-Delete
If referenced by quotations → `activo=false`. Otherwise hard-delete.
### Financing Formula
```
comisionTotal = monto × comision%
montoConComision = monto + comisionTotal
pagoMensual = montoConComision × (1 + tasa) / meses
ivaMensual = pagoMensual × 0.16
totalMensual = pagoMensual + ivaMensual
granTotal = totalMensual × meses
```
---
## Common Responses
### Success (paginated)
```json
{
"data": [...],
"meta": { "total": 45, "page": 1, "limit": 50, "pages": 1 }
}
```
### Error
```json
{ "error": "Message", "detail": "Details", "code": "ERROR_CODE" }
```
### HTTP Codes
200=OK, 201=Created, 204=No Content, 400=Bad Request, 401=Unauthorized, 404=Not Found, 409=Conflict, 500=Server Error
---
## Agent Workflows
### Create quotation
```
1. GET /catalogo?fase=0 → audit services
2. GET /catalogo?fase=1 → setup services
3. GET /catalogo?fase=2&tipoPago=mensual → ongoing services
4. POST /financiamiento/calcular → simulate payments
5. POST /cotizaciones → create quotation
6. GET /export/pdf/{id} → generate PDF
```
### Review pipeline
```
1. GET /cotizaciones?estado=borrador → pending
2. GET /cotizaciones?estado=enviada → awaiting
3. GET /cotizaciones?estado=aprobada → won
```
### Negotiate price
```
1. GET /cotizaciones/{id} → current state
2. PATCH /cotizaciones/{id}/precio → adjust price
3. GET /export/pdf/{id} → regenerate PDF
```
---
## MCP Integration — removed (2026-07-28)
The `/mcp` endpoint and the `api/app/mcp/` package no longer exist. Agents should use
the REST endpoints documented above, authenticating with `X-API-Key` or a Bearer JWT.
It was removed for three reasons: nobody was using it; its transport had been broken
for some time (it constructed a `StreamableHTTPServerTransport` per request with no
session handling, returning 500 even with valid credentials); and its unpinned SDK
took the entire API down in production when `mcp` released 2.0.0 and dropped
`Server.list_tools()`.
To bring it back: `git show edb500f5:api/app/mcp/server.py`. Pin the SDK to the 1.x
series and fix the transport before mounting it again.
---
## Environment
```
DB_HOST=localhost
DB_PORT=5432
DB_USER=postgres
DB_PASSWORD=postgres
DB_NAME=cotizador_e3
API_KEY=your-secret-api-key
JWT_SECRET=your-jwt-secret
```
## Run
```bash
pip install -r requirements.txt
uvicorn main:app --reload --host 0.0.0.0 --port 8000
# Swagger: http://localhost:8000/docs
# ReDoc: http://localhost:8000/redoc
# OpenAPI: http://localhost:8000/openapi.json
```