Skip to the content.

Seller-Hub-API

Documentación OpenAPI

Changelog

Índice


Inicio

Bienvenido a la documentación de la API de SellerHub para Proveedores. Esta API le permite integrar su sistema con la plataforma de GoodMeal para gestionar de forma automatizada:

En este documento encontrará los requisitos de integración, el flujo de operación y ejemplos prácticos para cada funcionalidad.

Requisitos previos de integración

Para integrar con SellerHub API, es importante cumplir con los siguientes requisitos:

Requisito Descripción
Credenciales Las credenciales son claves únicas que te proporcionamos para que puedas configurar tu información y acceder a consumir los servicios. Estas son proporcionadas por el equipo de GoodMeal y las debes resguardar de manera segura.
Tiendas Las tiendas, son donde cargaras tus productos, para ello estas serán configuradas por GoodMeal, quien luego te hará entrega de los accesos a cada una, para que puedas utilizar con la integración.
Parámetros de cuenta Son algunos parámetros necesarios para funcionar, uno es para poder cotizar delivery con tu servicio (Si es que es necesario) y otro para que podamos hacer ingreso de las órdenes de compra en tus sistemas. De esto se hace cargo GoodMeal.

Flujo de integración

Flujo de integración

Para usar el sistema de GoodMeal, debes conectar tu sistema a la API Seller, que te permitira:

Flujo de operación

proceso

Configuración de la integración

La integración con SellerHub API es simple de utilizar y solo requiere contar con el token de uso, y toda la configuración previa que hace GoodMeal, con eso listo, podemos ir a configurar parámetros esenciales para funcionar.

Configuración de token de uso

Todas las llamadas a la API deben hacer uso del header:

X-API-TOKEN : <token-de-uso>

Y además, el {clientId}, que es un valor que te entregamos, como identificador de usuario.

Configuración de firma HMAC

Con la intención de verificar autenticidad e integridad de los mensajes, se utiliza esta firma HMAC.

La firma se envía en la cabecera X-Signature, con formato sha256=<hex> o solo el <hex>.

Cómo se calcula la firma

Ejemplo en Node.js:

const crypto = require("crypto");

const secret = process.env.X_API_KEY; // el toke-de-uso asignado
const body = JSON.stringify(payload); // mismo texto que enviarás en el request

const hex = crypto.createHmac("sha256", secret).update(body).digest("hex");

// Cabecera
// X-Signature: sha256=<hex>

Ejemplo en terminal (OpenSSL):

echo -n '{"products":[]}' | openssl dgst -sha256 -hmac "$X_API_KEY" | awk '{print "sha256="$2}'

Ejemplo en Postman:

  1. Crea un Environment con x_api_key = valor de X-API-KEY (el toke-de-uso asignado).
  2. Body: raw → JSON.
  3. En Pre-request Script de la colección o de la request:
const secret = pm.environment.get("x_api_key");
const body = pm.request.body?.raw ?? "";

const hex = CryptoJS.HmacSHA256(body, secret).toString(CryptoJS.enc.Hex);

pm.request.headers.upsert({
  key: "X-Signature",
  value: `sha256=${hex}`,
});

Rutas cliente (/api/:clientId/...)

Cabecera Valor
X-API-KEY <token-de-uso> asignado al cliente
X-Signature HMAC-SHA256 del body con secreto X-API-KEY

Configurar parámetros de tu cuenta

Hay dos parámetros que son esenciales para operar, a continuación como configurar cada uno.

Configuración de cotización de delivery

Para poder cotizar delivery con tus servicios, debemos ser capaces de llamarlos, para ello deberás configurar el parámetro: base_url_notification_trip , para ello debes llamar al método:

POST /{clientId}/accounts/params

{
  'type': 'base_url_notification_trip',
  'value': '<url-de-acceso-para-cotizar-delivery>',
  'access': {
    'method': 'TOKEN',
    'token': {
        'api-token': '<api-token>'
    } 
  }
}

Esto es solo para configurarlo, luego en la sección de Gestión de orden, se comenta su utilización.

Configuración de envío de órdenes de compra

Ahora necesitamos saber a donde te enviaremos las órdenes de compra, para ello debemos configurar el siguiente parámetro: base_url_notification_purchase, para ello debes llamar al método:

POST /{clientId}/accounts/params

{
  'type': 'base_url_notification_purchase',
  'value': '<url-de-acceso-para-ordenes-de-compra>',
  'access': {
    'method': 'TOKEN',
    'token': {
        'api-token': '<api-token>'
    } 
  }
}

Esto es solo para configurarlo, luego en la sección de Gestión de orden, se comenta su uso.

Tipos de respuesta

Basamos todas las respuestas en el estándar HTML, por ende todas las respuestas de la API van por los códigos:

Idempotencia (event_id)

Varias operaciones de escritura aceptan el campo event_id como clave de idempotencia. Si reenvías la misma operación con el mismo valor, la API evita aplicarla dos veces.

Aspecto Detalle
Obligatorio No. Puedes omitir event_id; la petición se procesará, pero sin garantía de idempotencia ante reintentos.
Formato UUID (por ejemplo, 8faceb77-576a-42fc-92db-c15eb728f424). Debe ser generado y enviado por tu sistema (el cliente de la API).
Cuándo usarlo En cada operación que quieras poder reintentar de forma segura (timeouts de red, errores transitorios, etc.).
Reintentos Si una petición falló o no obtuviste respuesta y deseas reenviarla sin duplicar el efecto, reutiliza el mismo event_id. Para una operación nueva, genera un nuevo UUID.

Dónde enviarlo:

Operación Ubicación de event_id
Crear o editar productos (POST /{clientId}/products) Raíz del body: "event_id"
Actualizar stock (PUT /{clientId}/products/stock) Raíz del body: "event_id"
Actualizar órdenes (PUT /{clientId}/orders) Dentro de cada ítem: order.event_id

Recomendamos enviar event_id en integraciones automatizadas. El comportamiento con estados y respuestas HTTP descrito abajo aplica hoy a PUT /{clientId}/orders.

Estados de idempotencia (actualización de órdenes)

Al enviar order.event_id en PUT /{clientId}/orders, la API registra la operación y responde según uno de estos escenarios:

Estado HTTP Significado Qué hacer
Éxito (success) 200 OK La orden se procesó correctamente, o ya se había procesado antes con el mismo event_id y se devuelve la misma respuesta almacenada (sin volver a ejecutar la operación). Usar el body de la respuesta. Si fue un reintento, la orden no se duplicó.
En proceso (in_progress) 409 Conflict Otra petición con el mismo event_id se está procesando en este momento. Esperar unos segundos y reintentar con el mismo event_id y el mismo payload.
Inválido (invalid) 400 Bad Request El request no cumple las reglas de idempotencia (por ejemplo, el mismo order.event_id repetido dos veces dentro del arreglo orders de una sola petición). Corregir el payload. Mensaje típico: Duplicate order.event_id in request: <uuid>.
Fallido (failed) 400 Bad Request La operación no pudo completarse por un error de negocio o rechazo del servicio downstream. Revisar el mensaje de error. Si quieres intentar de nuevo como operación distinta, usa un nuevo event_id.
Reintentos agotados 429 Too Many Requests La API agotó los reintentos internos para procesar la operación (Max retry attempts exceeded). Esperar antes de reintentar (backoff). Puedes volver a enviar con el mismo event_id si no estás seguro de si la operación llegó a aplicarse.

Reglas adicionales:

Limites de consumo

Tamaño máximo por solicitud

Algunos endpoints procesan registros en lote dentro de un mismo request. El arreglo correspondiente no puede superar los siguientes límites; si necesitas enviar más elementos, divide la operación en varias peticiones.

Operación Método y ruta Campo Límite
Crear o editar productos POST /{clientId}/products products 50 productos por solicitud
Publicar o actualizar stock PUT /{clientId}/products/stock products 50 productos por solicitud
Actualizar órdenes PUT /{clientId}/orders orders 10 órdenes por solicitud

Si envías más registros de los permitidos, la API responderá con un error de validación (400).

Operación

Publicar productos

Ahora que ya has configurado tu tienda, vamos a cargar información en tu tienda, para que puedas vender.

Cargar y editar productos

Para poder vender a través de GoodMeal, debes cargar productos dentro de la tienda, para ello vamos a utilizar este método. Puedes enviar hasta 50 productos por solicitud en el arreglo products (ver Límites de consumo).

 {
  "method": "POST",
  "url": "/{clientId}/products",
  "headers": {
    "Content-Type": "application/json",
    "X-API-TOKEN": "",
  },
  "body": {
    "store_code": "<string>",
    "event_id" : "<string>",
    "products": [
      {
        "category": "<string>",
        "description": "<string>",
        "discount_percent": "<number>",
        "external_id": "<string>",
        "name": "<string>",
        "price_regular": "<number>",
        "quantity": "<number>",
        "size": "<string>",
        "sku": "<string>",
        "unit": "<string>",
        "upc_ean": "<string>"
      },
      {
        "category": "<integer>",
        "description": "<string>",
        "discount_percent": "<number>",
        "external_id": "<string>",
        "name": "<string>",
        "price_regular": "<number>",
        "quantity": "<number>",
        "size": "<string>",
        "sku": "<string>", 
        "unit": "<string>",
        "upc_ean": "<string>"
      }
    ]
  }
}

Este es un ejemplo, puedes ir a la definición de la API, para ver cada parte en detalle. El campo event_id es opcional; sirve como clave de idempotencia (ver Idempotencia (event_id)).

El campo size es opcional y se envía como string con la abreviatura del tamaño del producto (ver Tamaños).

Obtener productos

Para consultar los productos cargados en una tienda, utiliza este método. Debes indicar el store_code de la tienda y el número de page como parámetros de consulta. La respuesta viene paginada.

{
  "method": "GET",
  "url": "/{clientId}/products?store_code=<string>&page=<integer>",
  "headers": {
    "X-API-TOKEN": ""
  }
}

Parámetros

Parámetro Ubicación Descripción
store_code Query Código de la tienda a consultar.
page Query Número de página a consultar (comienza en 1).

Paginación

Campo Descripción
data Lista de productos de la página solicitada.
links.first URL de la primera página.
links.last URL de la última página. Puede ser null si aún no se conoce.
links.prev URL de la página anterior. null en la primera página.
links.next URL de la página siguiente. null en la última página.
meta.current_page Página actual.
meta.current_page_url URL de la página actual.
meta.from Índice del primer producto en la página.
meta.to Índice del último producto en la página.
meta.per_page Cantidad de productos por página.
meta.path URL base del endpoint sin parámetros de consulta.

Ejemplo de respuesta:

{
  "status": 200,
  "statusMsg": "Success",
  "data": [
    {
      "name": "<string>",
      "sku": "<string>",
      "categories": [
        {
          "id": "<integer>",
          "name": "<string>",
          "description": "<string|null>"
        }
      ],
      "description": "<string>",
      "discount_percentage": "<integer>",
      "unit": "<string|null>",
      "quantity": "<integer>",
      "weight": "<number|null>",
      "size": {
        "name": "<string>",
        "abbreviation": "<string>"
      },
      "image_url": "<string>",
      "price": "<integer>",
      "price_discount": "<integer>",
      "volume": "<number|null>"
    }
  ],
  "links": {
    "first": "<string|null>",
    "last": "<string|null>",
    "prev": "<string|null>",
    "next": "<string|null>"
  },
  "meta": {
    "current_page": "<integer>",
    "current_page_url": "<string>",
    "from": "<integer>",
    "path": "<string>",
    "per_page": "<integer>",
    "to": "<integer>"
  }
}

Puedes ir a la definición de la API para ver cada campo en detalle.

Categorías

Estas son las categorías de las que disponemos:

Nombres de categorías

Tamaños

El campo size corresponde al tamaño físico del producto. Se envía como string con la abreviatura del catálogo de tamaños disponibles. Este valor es importante porque se utiliza al cotizar y solicitar delivery: el sistema calcula la capacidad del envío según el tamaño acumulado de los productos de la orden.

Abreviatura Tamaño Referencia
xs Extra small Tamaño mínimo del catálogo
sm Small Se puede llevar con una mano (ej. una botella de agua)
md Medium Requiere una bolsa para transportarlo (ej. una bolsa de supermercado)
lg Large Requiere ambas manos (ej. un monitor)
xl Extra large Requiere varios viajes para transportar el contenido

Cómo elegir el tamaño correcto

Relación con el delivery

Los tamaños se utilizan para estimar si el pedido puede despacharse en vehículo de 2 ruedas (bicicleta, moto) o requiere vehículo de 4 ruedas (auto).

Cantidad y unidades

El campo unit y quantity_unit, corresponde a la unidad de medida y la cantidad de esa unidad, no son obligatorios, pero puede ayudar a mejorar la busqueda de un cliente.

Lista de unidades:

Actualizar stock

Ya agregaste los productos, ahora debemos agregar el stock necesario para poder vender, para ello debemos hacer uso de esta llamada. El arreglo products admite hasta 50 ítems por solicitud (ver Límites de consumo).

 {
  "method": "PUT",
  "url": "/{clientId}/products/stock",
  "headers": {
    "X-API-TOKEN": "",
    "Content-Type": "application/json"
  },
  "body": {
    "store_code": "<number>",
    "event_id" : "<string>",
    "products": [
      {
        "sku": "<string>",
        "stock": "<number>"
      },
      {
        "sku": "<string>",
        "stock": "<number>"
      }
    ]
  }
}

Cuidado!

Una vez cargues stock, estos comenzaran aparecer en la tienda, para su venta, asi que ten cuidado con cargar stock, si no quieres vender.

Mi stock publicado

Consulta el stock que tienes publicado en una tienda, junto con el estado de la publicación, la fecha y hora de publicación, el horario de atención configurado y el detalle de cada producto con su stock.

{
  "method": "GET",
  "url": "/{clientId}/products/stock?store_code=<string>",
  "headers": {
    "X-API-TOKEN": ""
  }
}

Parámetros

Parámetro Ubicación Descripción
store_code Query Código de la tienda a consultar.

Respuesta (data)

Campo Descripción
status Estado de la publicación. Valores: ACTIVE, INACTIVE.
post_date Fecha y hora de publicación en formato ISO 8601 (UTC), por ejemplo 2026-05-25T04:00:00.000000Z.
initial_pick_up_time Hora de apertura de la tienda (formato HH:mm).
end_pick_up_time Hora de cierre de la tienda (formato HH:mm).
products Lista de productos publicados en la tienda.

Productos (data.products[])

Campo Descripción
name Nombre del producto.
status Estado del producto. Valores: ACTIVE, INACTIVE.
current_stock Stock actual disponible para la venta.
initial_stock Stock con el que inició la publicación.
total_stock Stock total del producto en la publicación.

Ejemplo de respuesta

{
  "status": 200,
  "statusMsg": "Success",
  "data": {
    "id": 726,
    "restaurant_id": 225,
    "status": "ACTIVE",
    "post_date": "2026-05-25T04:00:00.000000Z",
    "initial_pick_up_time": "04:30",
    "end_pick_up_time": "23:30",
    "today_active": true,
    "store_routine_schedule_id": null,
    "products": [
      {
        "id": 2090,
        "name": "PAN 2025",
        "status": "ACTIVE",
        "current_stock": 8,
        "initial_stock": 1,
        "total_stock": 23,
        "shop_product_id": 622,
        "price": 1625,
        "real_price": 2500
      }
    ]
  }
}

Detalle de producto publicado

Obtiene el detalle de un producto publicado por su identificador. El product_id corresponde al campo id de cada ítem en la lista de GET /{clientId}/products/stock.

{
  "method": "GET",
  "url": "/{clientId}/products/stock/{product_id}?store_code=<string>",
  "headers": {
    "X-API-TOKEN": ""
  }
}

Parámetros

Parámetro Ubicación Descripción
product_id Path Identificador del producto publicado.
store_code Query Código de la tienda a consultar.

Respuesta (data)

Campo Descripción
id Identificador del producto publicado.
name Nombre del producto.
status Estado del producto. Valores: ACTIVE, INACTIVE.
current_stock Stock actual disponible para la venta.
initial_stock Stock con el que inició la publicación.
total_stock Stock total del producto en la publicación.
sell_stock Stock vendido.
shop_product_id Identificador del producto en la tienda.
type Tipo de producto (por ejemplo, MULTI_PRODUCT).
net_content Contenido neto del producto.
price Precio de venta.
real_price Precio de referencia.
store_id Identificador interno de la tienda.
is_cross_selling Indica si el producto es de venta cruzada.

Ejemplo de respuesta

{
  "status": 200,
  "statusMsg": "Success",
  "data": {
    "id": 2090,
    "product_id": null,
    "name": "PAN 2025",
    "shop_product_id": 622,
    "brand": null,
    "type": "MULTI_PRODUCT",
    "net_content": "10 lt",
    "price": 1625,
    "real_price": 2500,
    "store_id": 226,
    "current_stock": 8,
    "initial_stock": 1,
    "total_stock": 23,
    "status": "ACTIVE",
    "sell_stock": 11,
    "is_cross_selling": false,
    "tags": null,
    "expiration_date": null,
    "label": null,
    "max_stock": null
  }
}

Gestión de orden de compra

Ahora que publicaste productos, debemos procesar las órdenes de compra que te lleguen, de tus usuarios.

Cotizar delivery

Lo primero, si estás ofreciendo delivery, el usuario cotizará tu servicio, de todas las opciones que configuraste, tu deber es responderle un precio a cobrar y el usuario decidirá si lo acepta o no, esto lo veremos en el siguiente apartado.

Para ello te llegará una llamada a tu servicio de delivery configurado con lo siguiente:

{
    "code_id": "<string>",
    "delivery": {
      "address": "<string>",
      "delivery_code": "<string>",
      "detailed_address": {
        "aditional_address_information": "<string>",
        "city": "<string>",
        "commune": "<string>",
        "country": "<string>",
        "state": "<string>",
        "street_address_1": "<string>",
        "zip_code": "<string>"
      },
      "location": {
        "lat": "<string>",
        "log": "<string>"
      }
    },
    "products": [
      {
        "category": "<integer>",
        "description": "<string>",
        "discount_percent": "<number>",
        "external_id": "<string>",
        "name": "<string>",
        "price_regular": "<number>",
        "quantity": "<number>",
        "size": "sm",
        "sku": "<string>",
        "unit": "<string>",
        "upc_ean": "<string>"
      },
      {
        "category": "<integer>",
        "description": "<string>",
        "discount_percent": "<number>",
        "external_id": "<string>",
        "name": "<string>",
        "price_regular": "<number>",
        "quantity": "<number>",
        "size": "sm",
        "sku": "<string>",
        "unit": "<string>",
        "upc_ean": "<string>"
      }
    ],
    "store_code": "<string>"
}

Para el objeto delivery:

{
  "type": "object",
  "properties": {
    "address": {
      "type": "string",
      "description": "direccion del usuario, calle y numero"
    },
    "delivery_code": {
      "type": "string",
      "description": "el codigo del tipo de delivery que ingresaste al configurar tu cuenta"
    },
    "aditional_address_information": {
      "type": "string",
      "description": "informacion extra de la direccion, ej. Depto 101"
    },
    "city": {
      "type": "string",
      "description": "Ciudad del delivery"
    },
    "commune": {
      "type": "string",
      "description": "Comuna del delivery (CL)"
    },
    "country": {
      "type": "string",
      "description": "Pais del delivery (CL)"
    },
    "street_address_1": {
      "type": "string",
      "description": "Direccion completa"
    },
    "zip_code": {
      "type": "string",
      "description": "codigo ZIP de la direccion"
    },
    "lat": {
      "type": "string",
      "description": "Latitud de la direccion"
    },
    "log": {
      "type": "string",
      "description": "Longitud de la direccion"
    }
  }
}

Luego se incluye un arreglo de productos, para que se pueda calcular la capacidad del delivery, lo más importante de aquí:

Como respuesta se espera un 200 OK con el siguiente body:

{
  "total_price_trip": 1500,
  "extra_info": "Se va a enviar en caja de 8 unidades.",
  "expires": "2024-07-15T14:24:18.377Z",
  "duration": 38,
  "pickup_duration": 23
}

Donde:

Si no puedes realizar el delivery para la dirección o productos solicitados, responde con un código 4XX según lo indicado en Tipos de respuesta.

Recepcionar una orden de compra

Ahora que ya se pudo concretar la venta, debemos enviarte la orden de compra a la url que configuraste previamente.

el objeto delivery, solo aparece cuando la tienda tiene custom delivery activado.

Para ello, te enviaremos esto:

{
    "order_id": "<string>",
    "purchase_type": "<string>",
    "buyer_name": "<string>",
    "buyer_email": "<string|null>",
    "order_product_price": <number>,
    "created_at": "<string>",
    "store_code": "<string>",
    "order_status": "<string>",
    "delivery": {
      "address": "<string>",
      "delivery_code": "<string>",
      "detailed_address": {
        "aditional_address_information": "<string>",
        "city": "<string>",
        "commune": "<string>",
        "country": "<string>",
        "state": "<string>",
        "street_address_1": "<string>",
        "zip_code": "<string>"
      },
      "location": {
        "lat": "<string>",
        "log": "<string>"
      }
    },
    "products": [
      {
        "name": "<string>",
        "quantity": <number>,
        "sku": "<string>",
        "price": <number>,
        "type": "<string>"
      },
      {
        "name": "<string>",
        "quantity": <number>,
        "sku": "<string>",
        "price": <number>,
        "type": "<string>"
      }
    ]
}

Donde se incluye el {order_id}, que corresponde al ID de la orden, para poder identificarla más adelante, para hacer cambios.

Dentro hay dos objetos importantes:

Como respuesta se espera lo siguiente (o simplemente un 200 OK, sin body):

{
  "order_status": "string",
  "detail_order_status": "string",
  "delivery_status": "string",
  "detail_delivery_status": "string",
  "quote_id": "string",
  "tracking_url": "string"
}

Donde:

Actualizar una orden de compra

Mientras una orden de compra, este en estado PENDING, puedes editar cierta información. En cada petición puedes incluir hasta 10 órdenes en el arreglo orders (ver Límites de consumo).

Incluye order.event_id (UUID opcional) para idempotencia: si reenvías la misma actualización, la API devuelve la respuesta previa o indica si aún está en proceso (409). Ver Estados de idempotencia.

PUT /{clientId}/orders
{
    "orders": [
      {
        "delivery": {
          "quote_id": "QWEDW-IJEDA-JWED",
          "status": "PENDING",
          "tracking_url": "https://..."
        },
        "order": {
          "invoice_url": "https://...",
          "event_id" : "asdasd-asdasda-asdad-abscd",
          "order_id": "asdasd-asdasda-asdad-",
          "status": "PENDING"
        },
        "products": [
          {
            "available_quantity": 50,
            "quantity": 100,
            "sku": "23423423"
          }
        ],
        "store_code": "QQW@#D@D@#D"
      }
    ]
}

Los campos que puedes editar, estando en estado PENDING son:

Actualizar información de delivery

Una vez una orden este en estado APPROVED, puedes editar el objeto delivery con información relacionada con el estado del delivery:

Con el mismo método anterior (PUT /{storeId}/orders), pero además, cuando cambies el estado, se enviarán notificaciones PUSH al usuario, para que este sepa en qué estado está su pedido.

Cancelar una orden de compra

Puedes cancelar una orden por cualquier motivo, de diversas formas:

  1. Cuando recibes una orden, puedes enviarnos el estado CANCELED
    {
      "order_status": "CANCELED",
    ...
    }
    
  2. Cuando quieras actualizar la orden, nos envías el estado CANCELED:
    PUT /{clientId}/orders
    {
      "value": {
     "orders": [
       {
         "delivery": {
          ...
         },
         "order": {
           "event_id" : "asdasd-asdasda-asdad-asdfd",
           "order_id": "asdasd-asdasda-asdad-",
           "status": "CANCELED"
         },
         "products": [
           ...
         ],
         "store_code": "QQW@#D@D@#D"
       }
     ]
      }
    }
    
  3. Llamando al método de API, donde puedes enviar varias:
    PUT /{clientId}/orders/canceled
    {
      "orders": [
     {
       "order_id" : "8faceb77-576a-42fc-92db-c15eb728f424",
       "store_code": "e11a5997-404a-448c-bebe-31bdca2698b1"
     }
      ]
    }
    

Validación final

Una vez integrado con un ambiente previo, se hace un flujo de compra normal para validar que todos los puntos de integración están funcionando correctamente.

Cuando se autorice la integración por ambas partes, se procederá a mover configuraciones hacia un ambiente productivo para comenzar a operar.