API Reference
API REST pública de ForkCast. Integrá tu sistema con órdenes de compra, sincronizá datos con webhooks, y automatizá flujos de aprobación.
Introducción
La API de ForkCast permite a restaurantes y proveedores acceder a sus datos de forma programática. Podés consultar órdenes de compra, crear nuevas desde tu propio sistema, y recibir notificaciones en tiempo real via webhooks.
Todos los endpoints retornan JSON y usan autenticación por API key.
Todas las requests deben usar HTTPS
Body en JSON, respuestas en JSON
Fechas en formato ISO 8601 (UTC)
Request de ejemplo
curl https://app.forkcast.tech/api/v1/purchase-orders \
-H "X-API-Key: sk_live_xxxxxxxxxxxx"
Response
{
"data": [
{
"id": 42,
"status": "pending",
"date": "2026-05-20T10:00:00Z",
"items": [...]
}
],
"meta": { "timestamp": "..." }
}
Autenticación
La API usa autenticación por API key. Incluí tu key en cada request usando el header X-API-Key.
Las API keys se generan desde Configuración → API & Desarrolladores dentro de la app. Cada key tiene un scope (read o write) y puede tener fecha de expiración.
Formato de key
Las keys tienen el formato sk_live_ seguido de caracteres aleatorios. Guardá tu key de forma segura — no se puede recuperar después de generarla.
Header requerido
X-API-Key: sk_live_xxxxxxxxxxxx
401 Unauthorized — key ausente, inválida o revocada.
Con curl
curl https://app.forkcast.tech/api/v1/purchase-orders \
-H "X-API-Key: sk_live_xxxxxxxxxxxx" \
-H "Content-Type: application/json"
Scopes
Cada API key tiene un scope que define qué puede hacer. Usá el scope mínimo necesario para tu integración.
Puede consultar órdenes de compra y webhooks. Ideal para integraciones de reporting y dashboards externos.
Incluye todo lo de read más crear/actualizar órdenes y gestionar webhooks. Para integraciones ERP completas.
Permisos por endpoint
| Endpoint | read | write |
|---|---|---|
| GET /purchase-orders | ✓ | ✓ |
| GET /purchase-orders/:id | ✓ | ✓ |
| POST /purchase-orders | — | ✓ |
| PUT /purchase-orders/:id | — | ✓ |
| PUT /purchase-orders/:id/status | — | ✓ |
| POST /purchase-orders/bulk | — | ✓ |
| GET /webhooks | ✓ | ✓ |
| POST /webhooks | — | ✓ |
| DELETE /webhooks/:id | — | ✓ |
Base URL
Todos los endpoints de la API v1 usan la siguiente base URL. No hay versión en cada endpoint individual.
Base URL
https://app.forkcast.tech/api/v1
Ejemplo completo:
https://app.forkcast.tech/api/v1/purchase-orders
Órdenes de compra
/purchase-orders
Retorna las órdenes de compra asociadas al owner del API key. Los resultados están ordenados por fecha descendente.
Query params opcionales
| Param | Tipo | Descripción |
|---|---|---|
| status | string | Filtrar: pending, approved, rejected, delivered |
| from | date | Fecha desde (ISO 8601) |
| to | date | Fecha hasta (ISO 8601) |
Respuestas
curl "https://app.forkcast.tech/api/v1/purchase-orders?status=pending" \
-H "X-API-Key: sk_live_xxxxxxxxxxxx"
const res = await fetch(
'https://app.forkcast.tech/api/v1/purchase-orders?status=pending',
{ headers: { 'X-API-Key': process.env.FORKCAST_API_KEY } }
);
const { data } = await res.json();
import requests
res = requests.get(
"https://app.forkcast.tech/api/v1/purchase-orders",
params={"status": "pending"},
headers={"X-API-Key": FORKCAST_API_KEY},
)
orders = res.json()["data"]
Response 200
{
"data": [
{
"id": 42,
"restaurantId": 1,
"supplierId": 5,
"status": "pending",
"date": "2026-05-20T00:00:00Z",
"items": [
{
"name": "Harina 000",
"quantity": 10,
"unit": "kg",
"price": 850.00
}
],
"isBillable": false,
"createdAt": "2026-05-20T10:00:00Z"
}
],
"meta": {
"timestamp": "2026-05-20T12:00:00Z"
}
}
/purchase-orders/{id}
Obtiene el detalle completo de una orden de compra por ID. Solo podés acceder a órdenes que pertenecen al owner de tu API key.
Path params
| Param | Tipo | Descripción |
|---|---|---|
| id | integer | ID de la orden |
Respuestas
curl https://app.forkcast.tech/api/v1/purchase-orders/42 \
-H "X-API-Key: sk_live_xxxxxxxxxxxx"
const res = await fetch(
`https://app.forkcast.tech/api/v1/purchase-orders/${id}`,
{ headers: { 'X-API-Key': process.env.FORKCAST_API_KEY } }
);
const { data } = await res.json();
res = requests.get(
f"https://app.forkcast.tech/api/v1/purchase-orders/{order_id}",
headers={"X-API-Key": FORKCAST_API_KEY},
)
order = res.json()["data"]
/purchase-orders
Requiere scope write
Crea una nueva orden de compra en nombre del owner del API key. Si la key pertenece a un restaurante, la orden se crea como restaurante; si pertenece a un proveedor, como proveedor.
Body (JSON)
| Campo | Tipo | Req. | Descripción |
|---|---|---|---|
| items | array | ✓ | Productos de la orden |
| supplierId | integer | — | ID del proveedor |
| priceListId | integer | — | ID de lista de precios |
| franchiseId | integer | — | ID de franquicia (solo masters) |
| date | ISO 8601 | — | Fecha (default: ahora) |
| status | string | — | Estado inicial (default: pending) |
items[]
| Campo | Tipo | Req. |
|---|---|---|
| name | string | ✓ |
| quantity | number | ✓ |
| unit | string | ✓ |
| price | number | ✓ |
| packageSize | number | — |
| unitsPerPackage | number | — |
| packagingName | string | — |
| normalizedName | string | — |
Respuestas
curl https://app.forkcast.tech/api/v1/purchase-orders \
-X POST \
-H "X-API-Key: sk_live_xxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{
"supplierId": 5,
"items": [
{
"name": "Harina 000",
"quantity": 10,
"unit": "kg",
"price": 850.00
}
]
}'
const res = await fetch(
'https://app.forkcast.tech/api/v1/purchase-orders',
{
method: 'POST',
headers: {
'X-API-Key': process.env.FORKCAST_API_KEY,
'Content-Type': 'application/json',
},
body: JSON.stringify({
supplierId: 5,
items: [
{ name: 'Harina 000', quantity: 10, unit: 'kg', price: 850 }
],
}),
}
);
const { data } = await res.json();
res = requests.post(
"https://app.forkcast.tech/api/v1/purchase-orders",
json={
"supplierId": 5,
"items": [
{"name": "Harina 000",
"quantity": 10,
"unit": "kg",
"price": 850.0}
],
},
headers={"X-API-Key": FORKCAST_API_KEY},
)
order = res.json()["data"]
Response 201
{
"data": {
"id": 43,
"status": "pending",
"date": "2026-05-20T12:00:00Z",
"items": [...],
"isBillable": false
}
}
Response 422 · ORDER_OUT_OF_WINDOW
{
"code": "ORDER_OUT_OF_WINDOW",
"message": "Este proveedor solo recibe pedidos los días: 1, 2.",
"supplierId": 5,
"allowedDays": [1, 2],
"nextAllowedDate": "2026-06-22"
}
Se dispara cuando el proveedor (o su lista de precios) tiene orderWindowDays configurado y hoy no entra. Ver Ventana de recepción.
/purchase-orders/{id}/status
Requiere scope write
Actualiza el estado de una orden de compra. Solo podés modificar órdenes del owner de tu API key.
Estados válidos
Respuestas
curl https://app.forkcast.tech/api/v1/purchase-orders/42/status \
-X PUT \
-H "X-API-Key: sk_live_xxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{ "status": "delivered" }'
await fetch(
`https://app.forkcast.tech/api/v1/purchase-orders/${id}/status`,
{
method: 'PUT',
headers: {
'X-API-Key': process.env.FORKCAST_API_KEY,
'Content-Type': 'application/json',
},
body: JSON.stringify({ status: 'delivered' }),
}
);
/purchase-orders/{id}
Requiere scope write — solo órdenes en estado pending
Reemplaza los items de una orden de compra existente. La orden debe estar en estado pending — si ya fue aprobada o entregada, retorna 409.
Body (JSON)
| Campo | Tipo | Req. |
|---|---|---|
| items | Item[] | ✓ |
Misma estructura de items[] que en crear orden.
Respuestas
curl https://app.forkcast.tech/api/v1/purchase-orders/42 \
-X PUT \
-H "X-API-Key: sk_live_xxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{
"items": [
{ "name": "Harina 000", "quantity": 15,
"unit": "kg", "price": 850.00 }
]
}'
await fetch(
`https://app.forkcast.tech/api/v1/purchase-orders/${id}`,
{
method: 'PUT',
headers: {
'X-API-Key': process.env.FORKCAST_API_KEY,
'Content-Type': 'application/json',
},
body: JSON.stringify({ items }),
}
);
/purchase-orders/bulk
Requiere scope write
Crea múltiples órdenes de compra en una sola transacción. Si alguna falla, todas se revierten. Ideal para importaciones desde ERP o sistemas de facturación.
Body (JSON)
| Campo | Tipo | Req. | Descripción |
|---|---|---|---|
| orders | Order[] | ✓ | Array de órdenes a crear |
Cada elemento de orders[] tiene la misma estructura que el body de POST /purchase-orders.
Respuestas
curl https://app.forkcast.tech/api/v1/purchase-orders/bulk \
-X POST \
-H "X-API-Key: sk_live_xxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{
"orders": [
{
"supplierId": 5,
"items": [
{ "name": "Harina 000", "quantity": 10,
"unit": "kg", "price": 850 }
]
},
{
"supplierId": 8,
"items": [
{ "name": "Aceite", "quantity": 5,
"unit": "l", "price": 1200 }
]
}
]
}'
Response 201
{
"data": {
"message": "2 órdenes de compra creadas exitosamente",
"orders": [
{ "id": 43, "status": "pending", "isBillable": false, ... },
{ "id": 44, "status": "pending", "isBillable": false, ... }
]
}
}
Response 422 · BULK_ORDER_OUT_OF_WINDOW
{
"code": "BULK_ORDER_OUT_OF_WINDOW",
"message": "2 proveedor(es) fuera de ventana de recepción.",
"offenders": [
{ "supplierId": 5, "allowedDays": [1, 2], "nextAllowedDate": "2026-06-22" },
{ "supplierId": 8, "allowedDays": [3], "nextAllowedDate": "2026-06-24" }
]
}
El batch se rechaza completo (atomicidad) si al menos un proveedor cae fuera. offenders lista los proveedores ofensores con su próxima fecha disponible.
Perfil
/me
Retorna el perfil público del owner del API key. No requiere parámetros — la identidad se toma de la key.
Campos del response
| Campo | Tipo | Descripción |
|---|---|---|
| id | integer | ID del owner |
| name | string | Nombre comercial |
| username | string | Identificador único |
| address | string | null | Dirección |
| string | null | Email de contacto | |
| phone | string | null | Teléfono |
| active | boolean | Estado de la cuenta |
| deliveryZone (supplier) | object | null | Zona de entrega (solo proveedores) |
Respuestas
curl "https://app.forkcast.tech/api/v1/me" \
-H "X-API-Key: sk_live_xxxxxxxxxxxx"
Response 200 — restaurante
{
"data": {
"id": 6,
"name": "La Parrilla",
"username": "laparrilla",
"address": "Av. 9 de Julio 100",
"email": "info@laparrilla.com",
"phone": "+54911234567",
"active": true
}
}
/suppliers/:id
Retorna el perfil público de un proveedor. Solo disponible para restaurantes que hayan realizado al menos una orden de compra con ese proveedor.
Campos del response
| Campo | Tipo | Descripción |
|---|---|---|
| id | integer | ID del proveedor |
| name | string | Nombre comercial |
| username | string | Identificador único |
| address | string | Dirección |
| phone | string | null | Teléfono |
| string | null | Email de contacto | |
| deliveryZone | object | null | Zona de entrega |
| leadTimeDays | integer | null | Días de entrega |
| minOrderAmount | number | null | Monto mínimo de pedido |
| orderWindowDays | integer[] | null | Días en los que el proveedor recibe pedidos (0=domingo, 6=sábado). null o array vacío = sin restricción. Ver Ventana de recepción. |
| active | boolean | Estado de la cuenta |
Respuestas
curl "https://app.forkcast.tech/api/v1/suppliers/5" \
-H "X-API-Key: sk_live_xxxxxxxxxxxx"
Response 200
{
"data": {
"id": 5,
"name": "Distribuidora Sur",
"username": "distsur",
"address": "Ruta 3 km 20, GBA Sur",
"phone": "+54911000000",
"email": "ventas@distsur.com",
"deliveryZone": { "region": "GBA Sur" },
"leadTimeDays": 2,
"minOrderAmount": 5000,
"orderWindowDays": [1, 2],
"active": true
}
}
/restaurants/:id
Retorna el perfil público de un restaurante. Solo disponible para proveedores que hayan recibido al menos una orden de compra de ese restaurante.
Campos del response
| Campo | Tipo | Descripción |
|---|---|---|
| id | integer | ID del restaurante |
| name | string | Nombre comercial |
| username | string | Identificador único |
| address | string | null | Dirección |
| string | null | Email de contacto | |
| phone | string | null | Teléfono |
| active | boolean | Estado de la cuenta |
Respuestas
curl "https://app.forkcast.tech/api/v1/restaurants/6" \
-H "X-API-Key: sk_live_xxxxxxxxxxxx"
Response 200
{
"data": {
"id": 6,
"name": "La Parrilla",
"username": "laparrilla",
"address": "Av. 9 de Julio 100",
"email": "info@laparrilla.com",
"phone": "+54911234567",
"active": true
}
}
/franchises
Lista las franquicias activas de un restaurante master. Solo disponible para restaurantes con rol master.
Respuestas
curl "https://app.forkcast.tech/api/v1/franchises" \
-H "X-API-Key: sk_live_xxxxxxxxxxxx"
Response 200
{
"data": [
{
"id": 10,
"name": "Sucursal Norte",
"username": "norte",
"address": "Av. Norte 100",
"email": "norte@parrilla.com",
"phone": "+54911111111",
"active": true
}
]
}
Webhooks
/webhooks
Lista los webhooks configurados para el owner del API key.
Para más información sobre webhooks, payloads y verificación de firmas, ver la guía de webhooks.
curl https://app.forkcast.tech/api/v1/webhooks \
-H "X-API-Key: sk_live_xxxxxxxxxxxx"
/webhooks
Requiere scope write
Registra un nuevo webhook. El secret retornado se usa para verificar la firma de cada evento. Guardalo de forma segura — no se puede recuperar después.
Body (JSON)
| Campo | Tipo | Req. |
|---|---|---|
| url | string (URL) | ✓ |
| events | string[] | ✓ |
Eventos disponibles: order.created, order.status_updated
curl https://app.forkcast.tech/api/v1/webhooks \
-X POST \
-H "X-API-Key: sk_live_xxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{
"url": "https://tu-sistema.com/webhook",
"events": ["order.created", "order.status_updated"]
}'
Response 201
{
"data": {
"id": 1,
"url": "https://tu-sistema.com/webhook",
"events": ["order.created"],
"secret": "a1b2c3d4e5f6...",
"active": true
}
}
/webhooks/test
Requiere scope write
Dispara un evento de prueba a una URL para validar la integración antes de registrar un webhook permanente. Útil para verificar la verificación HMAC, la conectividad y el parseo del payload del lado del receptor.
Body (JSON)
| Campo | Tipo | Descripción |
|---|---|---|
| url | string | URL destino del test |
| event | string | Evento simulado (ej: order.created) |
Respuestas
statusCode recibido del destino.curl https://app.forkcast.tech/api/v1/webhooks/test \
-X POST \
-H "X-API-Key: sk_live_xxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{
"url": "https://tu-sistema.com/webhook",
"event": "order.created"
}'
Response 200
{
"data": {
"delivered": true,
"statusCode": 200,
"event": "order.created"
}
}
/webhooks/{id}
Requiere scope write
Elimina un webhook. No se recibirán más eventos en esa URL.
curl https://app.forkcast.tech/api/v1/webhooks/1 \
-X DELETE \
-H "X-API-Key: sk_live_xxxxxxxxxxxx"
Referencia
Ventana de recepción de pedidos
Los proveedores y las listas de precios pueden configurar días de recepción (orderWindowDays). Si se intenta crear un pedido fuera de esos días, la API responde con 422 ORDER_OUT_OF_WINDOW.
Formato de orderWindowDays
Array de enteros 0–6 donde 0 = domingo y 6 = sábado (criterio JavaScript Date.getDay()). null o array vacío significan sin restricción.
Precedencia
Cuando un pedido referencia una lista de precios, la ventana efectiva se calcula recorriendo la cadena de listas (de la hoja hacia las padres) buscando la primera orderWindowDays no nula. Si nadie en la cadena la define y la raíz es un proveedor, cae a la ventana del proveedor.
- Lista hoja (la del pedido) — si tiene
orderWindowDays, gana. - Cadena de listas padre — primera lista con
orderWindowDaysno nula. - Proveedor raíz — fallback a
supplier.orderWindowDays. - Si nadie define ventana → sin restricción.
Zona horaria
El cálculo del "día actual" se hace en America/Argentina/Buenos_Aires.
Ejemplo · Proveedor con ventana
{
"id": 5,
"name": "Distribuidora Sur",
"orderWindowDays": [1, 2]
}
Solo acepta pedidos lunes y martes.
Respuesta · pedido fuera de ventana
{
"code": "ORDER_OUT_OF_WINDOW",
"message": "Este proveedor solo recibe pedidos los días: 1, 2.",
"supplierId": 5,
"allowedDays": [1, 2],
"nextAllowedDate": "2026-06-22"
}
Errores
Todos los errores siguen el mismo formato. El campo code identifica el tipo de error; message es legible.
| HTTP | Significado |
|---|---|
| 400 | Datos del request inválidos |
| 401 | API key ausente o inválida |
| 403 | Scope insuficiente o sin acceso al recurso |
| 404 | Recurso no encontrado |
| 422 | Regla de negocio rechaza la operación (ej: ventana de recepción del proveedor cerrada) |
| 429 | Rate limit excedido |
| 500 | Error interno |
Códigos de error específicos (422)
| code | Significado |
|---|---|
| ORDER_OUT_OF_WINDOW | El proveedor o su lista de precios tiene orderWindowDays configurado y hoy no entra. Incluye supplierId, allowedDays y nextAllowedDate. |
| BULK_ORDER_OUT_OF_WINDOW | En un batch (/bulk), uno o más proveedores caen fuera de ventana. Incluye offenders con un objeto por proveedor ofensor. El batch se rechaza completo. |
Formato de error
{
"error": {
"message": "API key inválida o revocada.",
"code": "UNAUTHORIZED"
},
"meta": {
"timestamp": "2026-05-20T12:00:00Z",
"requestId": "uuid-..."
}
}
Rate limits
Los límites se aplican por API key. Las respuestas incluyen headers con el estado actual del límite.
Endpoints de consulta
Endpoints de modificación
Headers de rate limit
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 87
X-RateLimit-Reset: 1748347260
Al exceder el límite recibís un 429. Implementá backoff exponencial antes de reintentar.
Objetos
PurchaseOrder
| Campo | Tipo | Descripción |
|---|---|---|
| id | integer | ID único |
| restaurantId | integer | ID del restaurante |
| supplierId | integer | null | ID del proveedor |
| status | string | pending | approved | rejected | delivered | cancelled |
| date | ISO 8601 | Fecha del pedido |
| items | Item[] | Productos incluidos |
| isBillable | boolean | Si la orden es facturable. Se siembra de la lista de precios al crear y solo el vendedor o el master pueden cambiarlo (vía endpoint interno) mientras la orden esté en pending. |
| createdAt | ISO 8601 | Timestamp de creación |
| updatedAt | ISO 8601 | Última actualización |
Item
| Campo | Tipo | Descripción |
|---|---|---|
| name | string | Nombre del producto |
| quantity | number | Cantidad pedida |
| unit | string | kg, un, l, etc. |
| price | number | Precio unitario |
| packageSize | number | Tamaño de cada unidad física (default 1) |
| unitsPerPackage | number | Unidades por SKU (default 1) |
| packagingName | string | Nombre del packaging para display |
| normalizedName | string | Nombre normalizado para búsqueda |