Proxy de servidor
El navegador habla con tu backend y tu backend con Cord: ninguna llave llega a la página.
Cord Elements funciona de dos maneras: con una llave publicable (pk_) directo a Cord, o con proxyUrl, donde tu propio backend atiende las peticiones y llama a Cord con una llave de servidor. Usa el proxy cuando quieras que el Builder lea tu directorio de clientes, mande la cotización por correo o fije precios negociados: cosas que una pk_ nunca puede hacer.
El proxy vive en @flouviahq/node, el SDK de servidor. Ese paquete reemplaza al antiguo @flouviahq/elements/server, que sigue funcionando pero ya no recibe cambios.
Montarlo
npm install @flouviahq/node
// app/api/cord/[...path]/route.ts (Next.js App Router)
import { createElementsProxy } from '@flouviahq/node';
const proxy = createElementsProxy({
secretKey: process.env.CORD_SECRET_KEY!,
authorizeClients: async (req) => Boolean(await getSession(req)),
});
export const GET = proxy;
export const POST = proxy;
<CordProvider proxyUrl="/api/cord">
<CordBuilder />
</CordProvider>
getSession es tu propia función de sesión. createElementsProxy devuelve un handler (Request) => Promise<Response>, así que funciona igual en Next.js, Astro, Remix, Hono, Bun o Cloudflare Workers. cord init lo crea por ti en un proyecto de Next.js.
Qué atiende
| Petición del navegador | Llama a Cord | Condición |
|---|---|---|
GET {proxyUrl}/elements/config |
GET /api/v1/elements/config |
Siempre. |
GET {proxyUrl}/productos |
GET /api/v1/productos |
Siempre. |
POST {proxyUrl}/cotizaciones |
POST /api/v1/cotizaciones |
Siempre, con el cuerpo saneado. |
POST {proxyUrl}/elements/ai-draft |
POST /api/v1/elements/ai-draft |
Siempre. El stream de la IA se reenvía tal cual; archivo de hasta 4 MB. |
GET {proxyUrl}/clientes |
GET /api/v1/clientes?limit=200 |
Solo si authorizeClients devuelve true. Si no, 403 con code: "clients_require_proxy". |
Cualquier otra ruta o método responde 404. El proxy no reintenta: el navegador ya reintenta por su cuenta.
Opciones
| Opción | Detalle |
|---|---|
secretKey |
Obligatoria. Una llave sk_ o rk_. |
allowedOrigins |
Orígenes que pueden usar el proxy. Por defecto, solo el origen de la propia petición: un proxy abierto a cualquier sitio equivale a publicar tu llave. |
authorizeClients(request) |
Autoriza leer el directorio de clientes. Sin ella, nunca se lee. |
authorizeSellerFields(request) |
Autoriza los campos de vendedor al crear: cliente_id, send (enviar por correo), fiscal_currency, y en cada línea precio_negociado y costo_unitario. Sin ella, se descartan y la cotización nace como borrador, igual que con una pk_. |
baseUrl |
Origen de Cord. Default https://cordhq.app. |
fetch |
Para pruebas. |
const proxy = createElementsProxy({
secretKey: process.env.CORD_PROXY_KEY!,
allowedOrigins: ['https://portal.tuempresa.com'],
authorizeClients: async (req) => (await getSession(req))?.rol === 'vendedor',
authorizeSellerFields: async (req) => (await getSession(req))?.rol === 'vendedor',
});
El proxy reenvía la Idempotency-Key del navegador (el Builder manda una por intento de envío), y devuelve los errores de Cord con su code, request_id y doc_url.
Dale al proxy una llave restringida
El proxy no necesita una Secret Key con todos los permisos. Crea una llave restringida (rk_) en Desarrolladores › Llaves con solo esto:
| Recurso | Nivel | Para qué |
|---|---|---|
elements |
escritura | Configuración y llenado de partidas con IA |
productos |
lectura | El catálogo del Builder |
cotizaciones |
escritura | Crear la cotización |
clientes |
lectura | Solo si usas authorizeClients |
Si tu servidor se ve comprometido, esa llave no puede leer facturas, cobranza ni webhooks. Puedes además fijar las IPs de tu servidor y ponerle vencimiento. Ver Autenticación.
Depurar
Cada llamada del proxy a Cord queda en Desarrolladores › Registros del Workbench con su request_id, método, ruta, estado, duración e IP. El mismo request_id llega al navegador en el header Cord-Request-Id y en el cuerpo de un error.