All endpoints require JWT authentication via Authorization: Bearer <token> header.
Get All Orders
GET /api/admin/orders
Fetch recent orders with product details and shipping information
Authentication
Required. Token validated via verifyAdminToken(req) helper.
Response
Array of order objects (limited to 100 most recent)
Customer-facing readable ID
Order status: PENDING, CONFIRMED, PROCESSING, READY_TO_SHIP, PICKUP_REQUESTED, SHIPPED, DELIVERED, CANCELLED, RETURNED, REJECTED
PAID if confirmed or WOMPI payment, otherwise PENDING
COD (Cash on Delivery) or WOMPI (prepaid)
Parsed JSON shipping address
Parsed JSON billing address (if different from shipping)
Tracking information (if shipped)
Implementation
From orders.js:16-24:
const orders = await prisma.order.findMany({
orderBy: { createdAt: 'desc' },
include: {
items: {
include: { product: true } // Include product details
}
},
take: 100 // Limit for now
});
Example Response
{
"items": [
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"pin": "1234",
"customer_name": "María García",
"created_at": "2026-03-04T10:30:00Z",
"status": "CONFIRMED",
"total": 89900,
"payment_status": "PAID",
"payment_method": "WOMPI",
"shipping_info": {
"address": "Calle 123 #45-67",
"city": "Bogotá",
"phone": "3001234567"
},
"line_items": [
{
"name": "Jabón Natural de Lavanda",
"quantity": 2,
"sku": "KAIU-LAV-100"
}
],
"shipments": []
}
]
}
Generate Shipping Label
POST /api/admin/generate-label
Create shipment and generate shipping labels via Venndelo API
Request Body
Array of KAIU internal order UUIDs (not Venndelo IDs)
Response
SUCCESS when label is ready, PROCESSING if still generating
PDF URL for the shipping label (when status is SUCCESS)
Process Flow
- Resolve Venndelo IDs: Fetch
externalId from database for each order
- Create Shipment (if needed): Call Venndelo
/shipping/create-shipments
- Poll for Label: Retry up to 20 times (30 seconds max) waiting for label generation
- Send Email: Automatically send shipping confirmation with tracking number
From generate-label.js:92-113:
let attempts = 0;
const maxAttempts = 20;
while (attempts < maxAttempts) {
attempts++;
const payload = {
order_ids: venndeloIds,
format: "LABEL_10x15",
output: "URL"
};
const response = await fetch(
`https://api.venndelo.com/v1/admin/shipping/generate-labels`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Venndelo-Api-Key': VENNDELO_API_KEY
},
body: JSON.stringify(payload)
}
);
// ... check status and retry if PROCESSING
}
Example Request
const response = await fetch('https://api.kaiucol.com/api/admin/generate-label', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...'
},
body: JSON.stringify({
orderIds: ['550e8400-e29b-41d4-a716-446655440000']
})
});
Example Response
{
"status": "SUCCESS",
"data": "https://venndelo-labels.s3.amazonaws.com/label-12345.pdf"
}
Common Errors:
Insufficient balance: Venndelo prepaid account needs recharge
- Shipment creation fails if order already has active shipment
- Label generation requires valid shipping address and product dimensions
Request Pickup
POST /api/admin/request-pickup
Request carrier pickup for ready-to-ship orders
Request Body
Array of KAIU internal order UUIDs
Behavior
- Resolves Venndelo external IDs from database
- Calls Venndelo
/shipping/request-pickup
- Auto-updates order status to
PICKUP_REQUESTED
From request-pickup.js:68-77:
// AUTO-UPDATE LOCAL STATUS TO SHIPPED
await prisma.order.updateMany({
where: {
externalId: { in: venndeloIds }
},
data: {
status: 'PICKUP_REQUESTED',
updatedAt: new Date()
}
});
Example Request
await fetch('https://api.kaiucol.com/api/admin/request-pickup', {
method: 'POST',
headers: {
'Authorization': 'Bearer <token>',
'Content-Type': 'application/json'
},
body: JSON.stringify({
orderIds: ['550e8400-e29b-41d4-a716-446655440000']
})
});
Sync Shipments
POST /api/admin/sync-shipments
Synchronize tracking status for active orders from logistics providers
Authentication
Required JWT token.
Process
- Fetch Active Orders: Orders with status
PENDING through SHIPPED that have externalId
- Query Logistics: Calls
LogisticsManager.getShipmentStatus() for each order
- Update Status: Updates database if status changed
- Batch Limit: Processes 50 orders per request (ordered by oldest
updatedAt)
From sync-shipments.js:20-38:
const activeOrders = await prisma.order.findMany({
where: {
externalId: { not: null },
status: {
in: ['PENDING', 'CONFIRMED', 'PROCESSING', 'READY_TO_SHIP',
'PICKUP_REQUESTED', 'SHIPPED']
}
},
take: 50, // Límite por lote para evitar timeouts
orderBy: { updatedAt: 'asc' }, // Priorizar las que no se han actualizado recientemente
select: {
id: true,
externalId: true,
status: true,
carrier: true,
readableId: true,
trackingNumber: true,
paymentMethod: true
}
});
Response
Number of orders with status changes
Example Response
{
"success": true,
"processed": 50,
"updated": 3,
"details": [
{
"id": "1234",
"old": "PICKUP_REQUESTED",
"new": "SHIPPED"
},
{
"id": "1235",
"old": "SHIPPED",
"new": "DELIVERED"
}
]
}
Run this endpoint periodically (e.g., every 30 minutes) via a cron job to keep order statuses up-to-date.