Portales y Pagos
Portales públicos para clientes, checkout con Stripe y suscripciones con Chargebee
curl https://app.getplea.com/api/portal/invoice/abc123token
const invoice = await fetch('/api/portal/invoice/abc123token').then(r => r.json());
{
"id": "inv-uuid-1",
"number": "F-2026/0001",
"organizationName": "Bufete López & Asociados",
"clientName": "Elena Martínez Ruiz",
"status": "sent",
"items": [
{"description": "Consulta jurídica", "quantity": 1, "unitPrice": 150.00, "taxRate": 21}
],
"subtotal": 150.00,
"taxAmount": 31.50,
"total": 181.50,
"issueDate": "2026-03-01",
"dueDate": "2026-04-01",
"paymentEnabled": true,
"paymentUrl": "https://app.getplea.com/pay/inv-uuid-1"
}
{
"error": "Not Found",
"message": "Factura no encontrada o token expirado"
}
curl https://app.getplea.com/api/portal/quote/xyz789token
const quote = await fetch('/api/portal/quote/xyz789token').then(r => r.json());
{
"id": "quote-uuid-1",
"number": "P-2026/001",
"organizationName": "Bufete López & Asociados",
"clientName": "Elena Martínez Ruiz",
"status": "sent",
"items": [
{"description": "Honorarios procedimiento ordinario", "quantity": 1, "unitPrice": 2500.00, "taxRate": 21}
],
"subtotal": 2500.00,
"taxAmount": 525.00,
"total": 3025.00,
"validUntil": "2026-04-07",
"notes": "Presupuesto válido por 30 días"
}
curl -X POST https://app.getplea.com/api/portal/quote/xyz789token/accept
await fetch('/api/portal/quote/xyz789token/accept', { method: 'POST' });
{
"message": "Presupuesto aceptado correctamente",
"status": "accepted",
"acceptedAt": "2026-03-07T12:00:00Z"
}
curl -X POST https://app.getplea.com/api/portal/quote/xyz789token/reject \
-H "Content-Type: application/json" \
-d '{"reason": "El precio es demasiado alto"}'
await fetch('/api/portal/quote/xyz789token/reject', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ reason: 'El precio es demasiado alto' })
});
{
"message": "Presupuesto rechazado",
"status": "rejected"
}
curl -X POST https://app.getplea.com/api/payment/booking-checkout \
-H "Content-Type: application/json" \
-d '{
"bookingId": "booking-uuid-1",
"successUrl": "https://despacho.com/reserva-confirmada",
"cancelUrl": "https://despacho.com/reserva-cancelada"
}'
const { checkoutUrl } = await fetch('/api/payment/booking-checkout', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
bookingId: 'booking-uuid-1',
successUrl: 'https://despacho.com/reserva-confirmada',
cancelUrl: 'https://despacho.com/reserva-cancelada'
})
}).then(r => r.json());
{
"checkoutUrl": "https://checkout.stripe.com/c/pay/cs_test_a1b2c3...",
"sessionId": "cs_test_a1b2c3..."
}
curl -X POST https://app.getplea.com/api/payment/invoice-checkout \
-H "Content-Type: application/json" \
-d '{
"invoiceToken": "abc123token",
"successUrl": "https://despacho.com/pago-confirmado",
"cancelUrl": "https://despacho.com/pago-cancelado"
}'
const { checkoutUrl } = await fetch('/api/payment/invoice-checkout', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
invoiceToken: 'abc123token',
successUrl: 'https://despacho.com/pago-confirmado',
cancelUrl: 'https://despacho.com/pago-cancelado'
})
}).then(r => r.json());
{
"checkoutUrl": "https://checkout.stripe.com/c/pay/cs_live_x1y2z3...",
"sessionId": "cs_live_x1y2z3..."
}
curl https://app.getplea.com/api/subscription/status
const plans = await fetch('/api/subscription/status').then(r => r.json());
{
"plans": [
{"id": "starter", "name": "Starter", "price": 29, "currency": "EUR", "interval": "month"},
{"id": "professional", "name": "Professional", "price": 79, "currency": "EUR", "interval": "month"},
{"id": "enterprise", "name": "Enterprise", "price": 199, "currency": "EUR", "interval": "month"}
]
}
curl https://app.getplea.com/api/subscription \
-H "Authorization: Bearer TOKEN" \
-H "x-organization-id: ORG_ID"
const sub = await fetch('/api/subscription', {
headers: {
'Authorization': 'Bearer TOKEN',
'x-organization-id': 'ORG_ID'
}
}).then(r => r.json());
{
"id": "sub-uuid-1",
"planId": "professional",
"planName": "Professional",
"status": "active",
"currentPeriodStart": "2026-03-01",
"currentPeriodEnd": "2026-04-01",
"price": 79.00,
"currency": "EUR",
"seatsUsed": 3,
"seatsIncluded": 5
}
curl "https://app.getplea.com/api/reports/productivity?dateFrom=2026-01-01&dateTo=2026-03-31" \
-H "Authorization: Bearer TOKEN" \
-H "x-organization-id: ORG_ID"
const report = await fetch('/api/reports/productivity?dateFrom=2026-01-01&dateTo=2026-03-31', {
headers: {
'Authorization': 'Bearer TOKEN',
'x-organization-id': 'ORG_ID'
}
}).then(r => r.json());
{
"period": {"from": "2026-01-01", "to": "2026-03-31"},
"lawyers": [
{
"id": "user-uuid-1",
"name": "Ana López Fernández",
"casesOpened": 12,
"casesClosed": 8,
"tasksCompleted": 45,
"documentsUploaded": 67,
"invoicesIssued": 15,
"totalBilled": 28500.00
},
{
"id": "user-uuid-2",
"name": "Pedro Martín Sánchez",
"casesOpened": 8,
"casesClosed": 5,
"tasksCompleted": 32,
"documentsUploaded": 41,
"invoicesIssued": 10,
"totalBilled": 18200.00
}
]
}
curl "https://app.getplea.com/api/reports/financial-summary?dateFrom=2026-01-01&dateTo=2026-03-31" \
-H "Authorization: Bearer TOKEN" \
-H "x-organization-id: ORG_ID"
const summary = await fetch('/api/reports/financial-summary?dateFrom=2026-01-01&dateTo=2026-03-31', {
headers: {
'Authorization': 'Bearer TOKEN',
'x-organization-id': 'ORG_ID'
}
}).then(r => r.json());
{
"period": {"from": "2026-01-01", "to": "2026-03-31"},
"revenue": {
"total": 46700.00,
"paid": 38200.00,
"pending": 8500.00
},
"expenses": {
"total": 12350.00,
"approved": 10800.00,
"pending": 1550.00
},
"netIncome": 34350.00,
"invoicesIssued": 25,
"invoicesPaid": 20,
"averageInvoiceAmount": 1868.00
}
Endpoints para los portales públicos de facturas, presupuestos y clientes, así como la gestión de pagos mediante Stripe y suscripciones mediante Chargebee.
Portales públicos
Estos endpoints no requieren autenticación y se acceden mediante tokens únicos generados por la plataforma.
GET /api/portal/invoice/:token
Obtiene los datos de una factura para el portal público del cliente.
Token único de acceso a la factura.
GET /api/portal/quote/:token
Obtiene los datos de un presupuesto para que el cliente lo acepte o rechace.
Token único de acceso al presupuesto.
POST /api/portal/quote/:token/accept
El cliente acepta el presupuesto.
Token del presupuesto.
POST /api/portal/quote/:token/reject
El cliente rechaza el presupuesto.
Token del presupuesto.
Motivo del rechazo.
Pagos con Stripe
POST /api/payment/booking-checkout
Crea una sesión de checkout de Stripe para una reserva.
UUID de la reserva.
URL de redirección tras pago exitoso.
URL de redirección si cancela.
POST /api/payment/invoice-checkout
Crea una sesión de checkout para el pago de una factura.
Token público de la factura.
URL tras pago exitoso.
URL si cancela.
Suscripciones (Chargebee)
GET /api/subscription/status
Obtiene los planes disponibles y el estado general.
GET /api/subscription
Obtiene la suscripción actual de la organización.
Webhooks
Endpoints que reciben eventos de servicios externos. Estos endpoints no deben llamarse directamente; son configurados en los dashboards de Stripe, Chargebee y Recall.ai.
| Endpoint | Servicio | Verificación |
|---|---|---|
POST /api/webhooks/stripe | Stripe | Firma stripe-signature |
POST /api/webhooks/chargebee | Chargebee | IP allowlist |
POST /api/webhooks/recall | Recall.ai | Token |
POST /api/webhooks/resend/inbound | Resend | Firma |
Informes
GET /api/reports/productivity
Informe de productividad por abogado.
Desde fecha.
Hasta fecha.
GET /api/reports/financial-summary
Resumen financiero del período.
Desde fecha.
Hasta fecha.
Last updated today