Home

Logistics API Documentation

Guía práctica para consumir la API local del sistema de paquetería

Ver OpenAPI (YAML)

Quick Reference

This documentation describes the most used endpoints for integration: Authentication, Products and Orders (Pedidos). Examples show request shape, response shape and common errors.

OpenAPI (machine-readable): paqueteria_api.yaml.

Quickstart

Minimal steps to call the API successfully:

  1. Obtain a JWT token: POST /api/auth/login with { "email", "password" }.
  2. Take the token from the login response at response.data.token (important: the token is inside data.token, not at the top level).
  3. Call protected endpoints adding header: Authorization: Bearer <token>.
  4. When creating orders, provide required address fields and a unique numero_orden, and ensure the product has enough stock.

Example: get token (curl)

curl -s -X POST "{API_BASE_URL}/api/auth/login" \ -H "Content-Type: application/json" \ -d '{"email":"user@example.com","password":""}'

Login response (important)

{
    "success": true,
    "message": "Login exitoso",
    "data": { "token": "<JWT_TOKEN>" }
}

Note: always use the token value located at data.token when setting the Authorization header.

Authentication (Login)

Obtain a JWT token. NOTE: the HTTP response envelope is { success, message, data: { token } }.

Endpoint

POST /api/auth/login

Request body (JSON)

FieldTypeRequiredDescription
emailstring (email)yesUser email
passwordstringyesUser password

Example request

{
    "email": "admin@example.com",
    "password": "123456"
}

Success response (200)

{
    "success": true,
    "message": "Login exitoso",
    "data": { "token": "<JWT_TOKEN>" }
}

Usage

Authorization: Bearer <JWT_TOKEN from response.data.token>

Security note: never embed real credentials or long-lived tokens in public documentation or examples. Use placeholders and environment variables when running commands.

Geographic & Reference Data (GeoInfo)

Endpoint to retrieve reference lists used by the front-end selects: countries, departments, municipalities, neighborhoods and currencies.

Endpoint

GET /api/geoinfo/listar

Returns an object data containing arrays for paises (countries), departamentos (departments), municipios (municipalities), barrios (neighborhoods) and monedas (currencies). Useful to initialize forms and dependent selects.

Response example

{
    "success": true,
    "message": "GeoInfo listed",
    "data": {
        "paises": [{ "id": 1, "nombre": "Nicaragua", "codigo_iso": "NI" }],
        "departamentos": [{ "id": 1, "nombre": "Managua", "id_pais": 1 }],
        "municipios": [{ "id": 1, "nombre": "Managua", "id_departamento": 1 }],
        "barrios": [{ "id": 1, "nombre": "Altamira", "id_municipio": 1 }],
        "monedas": [{ "id":1, "codigo":"USD", "nombre":"US Dollar", "tasa_usd":"1.0000" }]
    }
}

Geographic Data Management (CRUD)

Endpoints to manage Paises, Departamentos, Municipios, and Barrios. All endpoints support GET, POST, PUT, DELETE.

Paises

GET /api/geoinfo/paises?id={id} (optional)
POST /api/geoinfo/paises
PUT /api/geoinfo/paises?id={id}
DELETE /api/geoinfo/paises?id={id}
Payload (POST/PUT)
{
    "nombre": "Nombre del Pais",
    "codigo_iso": "NP"
}

Departamentos

GET /api/geoinfo/departamentos?id={id} (optional)
POST /api/geoinfo/departamentos
PUT /api/geoinfo/departamentos?id={id}
DELETE /api/geoinfo/departamentos?id={id}
Payload (POST/PUT)
{
    "nombre": "Nombre del Departamento",
    "id_pais": 1
}

Municipios

GET /api/geoinfo/municipios?id={id} (optional)
POST /api/geoinfo/municipios
PUT /api/geoinfo/municipios?id={id}
DELETE /api/geoinfo/municipios?id={id}
Payload (POST/PUT)
{
    "nombre": "Nombre del Municipio",
    "id_departamento": 1
}

Barrios

GET /api/geoinfo/barrios?id={id} (optional)
POST /api/geoinfo/barrios
PUT /api/geoinfo/barrios?id={id}
DELETE /api/geoinfo/barrios?id={id}
Payload (POST/PUT)
{
    "nombre": "Nombre del Barrio",
    "id_municipio": 1
}

Products (CRUD)

Manage products. Mutating endpoints require a valid Authorization header.

List products

GET /api/productos/listar

Returns a list of products with aggregated stock (field stock_total).

Optional query parameter: include_stock=1 — when set, the response includes a stock_entries array for each product with recent stock movements (fields: id, id_producto, id_usuario, cantidad, updated_at).

Response (200)

{
    "success": true,
    "data": [
        { "id": 1, "nombre": "Matcha Slim", "precio_usd": "25.00", "stock_total": 2 },
        { "id": 2, "nombre": "Protein Shake", "precio_usd": "40.00", "stock_total": 60 }
    ]
}
Response with include_stock=1 (example)
{
    "success": true,
    "data": [
        {
            "id": 2,
            "nombre": "Protein Shake",
            "precio_usd": "40.00",
            "stock_total": "48",
            "stock_entries": [
                { "id": 28, "id_producto": 2, "id_usuario": 5, "cantidad": -11, "updated_at": "2025-11-19 11:57:38" },
                { "id": 9,  "id_producto": 2, "id_usuario": 1, "cantidad": 29,  "updated_at": "2025-10-31 12:56:52" }
            ]
        }
    ]
}

Create product

POST /api/productos/crear
FieldTypeRequiredNotes
nombrestringyesUnique-ish name used by lookup functions
descripcionstringnoOptional
precio_usdnumbernoDecimal, stored as string in responses
stockintegernoOptional initial stock quantity — when provided the API inserts a stock movement for the authenticated user (or uses FALLBACK_USER_FOR_STOCK if configured).

Example create request

{
    "nombre": "Producto X",
    "descripcion": "Descripción opcional",
    "precio_usd": 9.5,
    "stock": 12
}

Success response

{
    "success": true,
    "message": "Producto creado correctamente.",
    "data": { "id": 42, "stock_inserted": 99 }
}

Orders

Endpoints to create, search and list orders. The server stores coordinates as a POINT; for API requests provide coordinates as "lat,long" or as numeric latitud and longitud fields.

Search order by numero_orden

GET /api/pedidos/buscar?numero_orden=<NUMBER>

Requires Authorization header: Authorization: Bearer <token>. Returns the order data (latitud/longitud as numbers) when found.

Example (curl)
curl -s "{API_BASE_URL}/api/pedidos/buscar?numero_orden=90001" \ -H "Authorization: Bearer <JWT_TOKEN>"
Success response (200)
{
    "success": true,
    "message": "Order found",
    "data": {
        "numero_orden": "90001",
        "destinatario": "Test Customer",
        "telefono": "0999999999",
        "id_pais": 3,
        "latitud": -0.180653,
        "longitud": -78.467838,
        "nombre_estado": "Pending"
    }
}

Create order

POST /api/pedidos/crear

Important notes

  • The API response envelope is { success, message, data }.
  • Fields id_moneda, id_vendedor and id_proveedor are stored in pedidos and have foreign key constraints — they must reference existing rows.
  • Products are stored in pedidos_productos (pivot). The API accepts the simple format using top-level producto or producto_id plus cantidad. Internally the model supports creating an order with multiple items (see crearPedidoConProductos in the model).
  • Stock validation: the system checks stock (via DB triggers and application checks). If stock is insufficient the request will fail with an error message.

Request fields (common)

Below are the main fields related to the pedidos table and how to pass them in the JSON payload when creating an order. Fields generated by the server (like id and fecha_ingreso) should not be supplied.

FieldTypeRequiredDescription / how to provide it
idintegerserverPrimary key generated by the server — do not provide on create.
fecha_ingresodatetimeserverInsertion timestamp set by the server — do not provide on create.
numero_ordenintegeryesUnique order number (your system should ensure uniqueness).
destinatariostringyesRecipient name.
telefonostringyesPhone number for the recipient.
precio_localnumbernoLocal currency price (optional). If provided, include as decimal (e.g., 120.50).
precio_usdnumbernoPrice in USD (optional).
id_paisintegerrecommendedCountry id — use the numeric id from /api/geoinfo/listarpaises.
id_departamentointegerrecommendedDepartment id — use the numeric id from /api/geoinfo/listardepartamentos.
id_municipiointegerrecommendedMunicipality id — use the numeric id from /api/geoinfo/listarmunicipios.
id_barriointegernoNeighborhood id — optional; get the numeric id from /api/geoinfo/listarbarrios if available.
direccionstringnoFull address.
zonastringnoOptional zone/neighborhood descriptor (free text).
comentariostringnoOptional comments about the order.
coordenadasstringyesLatitude and longitude as "lat,long" (or provide numeric latitud and longitud fields).
id_estadointegerrecommendedStatus id referencing estados (if your system uses it). Use valid id from your statuses table.
id_monedaintegerrecommendedFK to monedas.id. Use the numeric id from /api/geoinfo/listarmonedas.
id_vendedorintegeroptionalFK to usuarios.id for seller/repartidor. Use numeric user IDs from your users administration or /api/usuarios/listar when available.
id_proveedorintegeroptionalFK to usuarios.id for provider — provide a numeric user id; if omitted the authenticated user may be used.
productosarraynoArray of items: each item { producto_id: integer, cantidad: integer }. For single-product requests you may use top-level producto_id + cantidad.

Example create request (single product)

{
    "numero_orden": 90001,
    "destinatario": "Cliente Prueba",
    "telefono": "0999999999",
    "producto_id": 12,
    "cantidad": 1,
    "coordenadas": "-0.180653,-78.467838",
    "direccion": "Calle Falsa 123",
    "zona": "Zona A",
    "precio_local": 120.50,
    "precio_usd": 30.12,
    "id_moneda": 1,
    "id_vendedor": 5,
    "id_proveedor": 6,
    "id_pais": 3,
    "id_departamento": 5,
    "id_municipio": 12,
    "id_barrio": 7,
    "comentario": "Entrega en horario de oficina"
}

Example create request (multiple products)

Provide product IDs in the productos array. Each item must include producto_id (integer) and cantidad (int).

{
    "numero_orden": 90002,
    "destinatario": "Cliente Prueba",
    "telefono": "0999999999",
    "productos": [
        { "producto_id": 12, "cantidad": 2 },
        { "producto_id": 13, "cantidad": 1 }
    ],
    "coordenadas": "-0.180653,-78.467838",
    "direccion": "Calle Falsa 123",
    "id_moneda": 1,
    "id_pais": 3,
    "id_departamento": 5,
    "id_municipio": 12
}

Working example (real payload)

Example JSON that has been tested with the /api/pedidos/crear endpoint — it uses the productos array with producto_id (product 2: "Protein Shake").

{
                            "numero_orden": 1700385600,
                            "destinatario": "Proveedor Prueba",
                            "telefono": "0999999999",
                            "productos": [
                                { "producto_id": 2, "cantidad": 1 }
                            ],
                            "coordenadas": "-0.180653,-78.467838",
                            "direccion": "Calle Falsa 123",
                            "zona": "Centro",
                            "id_moneda": 1,
                            "pais": 3,
                            "departamento": 5,
                            "id_municipio": 12,
                            "id_barrio": 7,
                            "comentario": "Pedido de prueba via Postman"
                        }

Example (curl) - create order

curl -s -X POST "{API_BASE_URL}/api/pedidos/crear" \ -H "Content-Type: application/json" \ -H "Authorization: Bearer <JWT_TOKEN>" \ -d '{ "numero_orden": 90001, "destinatario": "Cliente Prueba", "telefono": "0999999999", "producto_id": 12, "cantidad": 1, "coordenadas": "-0.180653,-78.467838", "id_municipio": 12 }'

Usage rules / Quick tips

  • Always use numeric identifiers for geographic fields (id_pais, id_departamento, id_municipio, id_barrio). Obtain these ids from /api/geoinfo/listar.
  • Do not send server-managed fields such as id or fecha_ingreso — the server sets them.
  • You can send a single-product order using producto_id + cantidad, or multiple products using the productos array (each item: { producto_id, cantidad }).
  • If you omit id_proveedor, the API may use the authenticated user (from the token) when applicable.
  • Coordinate format: "lat,long" or provide numeric latitud and longitud fields.
  • Price fields (precio_local, precio_usd) are optional but must be numeric if provided.
  • numero_orden must be unique; duplicate numbers will return an error.
  • The system validates stock before creating the order — if stock is insufficient the request will fail.

Possible successful response

{
    "success": true,
    "message": "Order created successfully.",
    "data": 1700385600
}

Examples of error responses

{
    "success": false,
    "message": "Error inserting order: Insufficient stock for product ID 11. Available: 0, required: 1"
}

{
    "success": false,
    "message": "Error inserting order: Cannot add or update a child row: a foreign key constraint fails (...)"
}

Bulk Orders (Import multiple orders)

Creates multiple orders in a single request from a JSON payload. This endpoint is intended for integrations and batch imports (for example, converting CSV to JSON before sending). The endpoint accepts a top-level JSON object with a pedidos array. Each element must follow the same order structure used by /api/pedidos/crear.

Endpoint

POST /api/pedidos/multiple

Request body (JSON)

Send a JSON object with a pedidos array. Each order must include numero_orden, recipient details and at least one product in the productos array. Coordinates may be provided as a string "lat,long" or as numeric fields latitud and longitud.

Example request (batch of 5 orders)

{
    "pedidos": [
        {
            "numero_orden": 1001,
            "destinatario": "Customer One",
            "telefono": "12345678",
            "productos": [
                { "producto_id": 1, "cantidad": 2 }
            ],
            "coordenadas": "-34.500000,-58.400000",
            "direccion": "Street 1 #123",
            "id_pais": 1,
            "id_departamento": 2
        },
        {
            "numero_orden": 1002,
            "destinatario": "Customer Two",
            "telefono": "87654321",
            "productos": [
                { "producto_id": 2, "cantidad": 1 },
                { "producto_id": 3, "cantidad": 1 }
            ],
            "latitud": -34.600000,
            "longitud": -58.500000,
            "direccion": "Evergreen Ave 742",
            "id_pais": 1,
            "id_departamento": 2
        },
        {
            "numero_orden": 1003,
            "destinatario": "Customer Three",
            "telefono": "55512345",
            "productos": [
                { "producto_id": 1, "cantidad": 1 }
            ],
            "coordenadas": "-34.510000,-58.410000",
            "direccion": "Street 3 #45",
            "id_pais": 1,
            "id_departamento": 2
        },
        {
            "numero_orden": 1004,
            "destinatario": "Customer Four",
            "telefono": "60070080",
            "productos": [
                { "producto_id": 4, "cantidad": 1 }
            ],
            "latitud": -34.520000,
            "longitud": -58.420000,
            "direccion": "Boulevard 9",
            "id_pais": 1,
            "id_departamento": 2
        },
        {
            "numero_orden": 1005,
            "destinatario": "Customer Five",
            "telefono": "70080090",
            "productos": [
                { "producto_id": 2, "cantidad": 2 }
            ],
            "coordenadas": "-34.530000,-58.430000",
            "direccion": "Route 5",
            "id_pais": 1,
            "id_departamento": 2
        }
    ]
}

Response format

The endpoint returns a JSON object with a results array. Each item corresponds to one submitted order and contains numero_orden, success (boolean) and either id_pedido when the insertion succeeded or error with a message describing why that particular order failed. Processing continues for remaining orders even if some fail.

Example response (success + errors)

{
    "results": [
        { "numero_orden": 1001, "success": true, "id_pedido": 201 },
        { "numero_orden": 1002, "success": false, "error": "Insufficient stock for product ID 2" },
        { "numero_orden": 1003, "success": true, "id_pedido": 203 },
        { "numero_orden": 1004, "success": true, "id_pedido": 204 },
        { "numero_orden": 1005, "success": true, "id_pedido": 205 }
    ]
}

Example (curl)

curl -s -X POST "{API_BASE_URL}/api/pedidos/multiple" \ -H "Content-Type: application/json" \ -H "Authorization: Bearer <JWT_TOKEN>" \ -d '@pedidos_batch.json'

Save the example payload to pedidos_batch.json locally and use the curl command above. The server returns HTTP 200 with the per-order results array when the top-level JSON is valid. If the JSON is malformed the API returns HTTP 400.

Troubleshooting & tips

  • If you get FK errors when creating orders, check that id_moneda, id_vendedor and id_proveedor exist in their respective tables.
  • To create a product and give it stock (dev): create product via /api/productos/crear, then use the stock UI or insert into stock table.
  • Coordinates must be provided; the API will reject requests missing valid coordinates.
  • Address fields required: the API validates pais, departamento and municipio. If any are missing you will receive a validation error listing the missing fields.
  • numero_orden must be unique. If you get "El número de orden ya existe", use a different number.
  • If you receive "Stock insuficiente" for a product, either increase stock for that product (via stock creation) or reduce the requested cantidad.