Developers

Herco Hub API

A REST API for connecting your store, ERP or warehouse system to the Herco Hub. Push your catalog and stock, read marketplace orders, send tracking codes back, and receive signed webhooks whenever something changes in Brazil.

Base URL

https://hercohub.com

Format

JSON over HTTPS. UTF-8. No caching.

Authentication

Every request carries an API key generated inside the Hub (Developers page). The key is stored only as a hash, is scoped to a single company, and can be revoked at any time. Keys are secret — use them from your server, never from a browser.

Headers
Authorization: Bearer hk_live_xxxxxxxxxxxxxxxxxxxxxxxx
Content-Type: application/json

Scopes

products:readRead catalog and inventory.
products:writeCreate and update products.
orders:readRead orders and post shipment updates.
inventory:writePush stock quantities.

Endpoints

GET/api/public/v1/productsproducts:read

List the catalog stored in the Hub.

Returns up to 500 products of the company that owns the API key, ordered by SKU.

curl "https://hercohub.com/api/public/v1/products" \
  -H "Authorization: Bearer hk_live_YOUR_KEY"
Response
{
  "data": [
    {
      "id": "3f1c...",
      "sku": "ABC-1",
      "title_en": "Blue Mug",
      "title_pt": "Caneca Azul",
      "category": "Home",
      "brand": "Acme",
      "cost_usd": 9.9,
      "stock": 42,
      "weight_kg": 0.4,
      "active": true,
      "updated_at": "2026-09-01T12:00:00Z"
    }
  ]
}
POST/api/public/v1/productsproducts:write

Create or update products in bulk (max 500 per request).

Products are matched by SKU. Every call is recorded as an auditable sync run visible in Monitoring.

curl -X POST "https://hercohub.com/api/public/v1/products" \
  -H "Authorization: Bearer hk_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "products": [ { "sku": "ABC-1", "title_en": "Blue Mug", "title_pt": "Caneca Azul", "category": "Home", "brand": "Acme", "cost_usd": 9.9, "weight_kg": 0.4, "ncm": "6912.00.00", "stock": 100, "active": true } ] }'
Response
{ "imported": 1, "rejected": 0, "errors": [] }
invalid_jsonBody is not valid JSON.
expected_products_arraySend an array, or an object with a products array.
batch_too_largeMore than 500 rows in one call.
GET/api/public/v1/ordersorders:read

Marketplace orders with their items.

Sorted by order date, newest first. Use since for incremental reads instead of full scans.

Query parameters

sinceISO 8601Only orders placed at or after this timestamp.
statusstringFilter by order status.
limitnumberPage size. Default 100, maximum 200.
curl "https://hercohub.com/api/public/v1/orders?limit=50" \
  -H "Authorization: Bearer hk_live_YOUR_KEY"
Response
{
  "data": [
    {
      "id": "9a2f...",
      "external_order_id": "ML-123",
      "status": "shipped",
      "buyer_city": "São Paulo",
      "buyer_state": "SP",
      "total_brl": 249.9,
      "fees_brl": 37.4,
      "freight_brl": 22.0,
      "tracking_code": "BR123456789BR",
      "carrier": "HomeDelivery",
      "placed_at": "2026-09-01T14:22:00Z",
      "delivered_at": null,
      "order_items": [
        { "sku": "ABC-1", "title": "Caneca Azul", "quantity": 2, "unit_price_brl": 124.95 }
      ]
    }
  ]
}
GET/api/public/v1/inventoryproducts:read

Stock per product and per location.

curl "https://hercohub.com/api/public/v1/inventory" \
  -H "Authorization: Bearer hk_live_YOUR_KEY"
Response
{
  "data": [
    {
      "sku": "ABC-1",
      "stock": 120,
      "inventory_levels": [
        { "quantity": 120, "location": { "name": "Miami DC", "kind": "us_warehouse" } }
      ]
    }
  ]
}
POST/api/public/v1/inventoryinventory:write

Push stock quantities per SKU and location (max 500 items).

The total stock of the SKU is recalculated, stock alerts are re-evaluated and the inventory.updated event is emitted. location accepts a location name or kind; when omitted the US warehouse is used.

curl -X POST "https://hercohub.com/api/public/v1/inventory" \
  -H "Authorization: Bearer hk_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "items": [ { "sku": "ABC-1", "quantity": 120, "location": "us_warehouse" } ] }'
Response
{
  "updated": 1,
  "rejected": 1,
  "errors": [ { "sku": "XYZ-9", "message": "unknown_sku" } ]
}
sku_requiredItem sent without a SKU.
unknown_skuSKU does not exist in this company's catalog.
no_location_configuredNo matching inventory location is configured.
POST/api/public/v1/shipmentsorders:read

Send tracking code and status per order (max 200 items).

Identify the order by external_order_id or by order_id (the Hub UUID). Accepted status values: confirmed, shipped, in_transit, delivered, cancelled, returned. Defaults to shipped. Each update emits order.status_changed.

curl -X POST "https://hercohub.com/api/public/v1/shipments" \
  -H "Authorization: Bearer hk_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "shipments": [ { "external_order_id": "ML-123", "tracking_code": "BR123456789BR", "carrier": "HomeDelivery", "status": "shipped" } ] }'
Response
{ "updated": 1, "rejected": 0, "errors": [] }
order_reference_requiredNeither external_order_id nor order_id was sent.
order_not_foundNo order with that reference in this company.

Outbound webhooks

Register your URL and the events you want on the Developers page. The Hub posts JSON to that URL and signs the raw body with your endpoint secret. Answer 2xx quickly; failed deliveries are retried with backoff.

Delivery
POST https://your-system.com/herco/webhook
x-herco-event: order.status_changed
x-herco-signature: 6f1b...   # HMAC-SHA256 (hex) of the raw body
content-type: application/json

{ "order_id": "9a2f...", "external_order_id": "ML-123", "status": "shipped", "tracking_code": "BR123456789BR" }

Events

order.createdA new marketplace order arrived in the Hub.
order.status_changedOrder status, tracking code or carrier changed.
shipment.createdA shipping label was generated for an order.
inventory.updatedStock quantities changed for one or more SKUs.

Verifying the signature

import { createHmac, timingSafeEqual } from "node:crypto";

app.post("/herco/webhook", express.raw({ type: "*/*" }), (req, res) => {
  const signature = req.header("x-herco-signature") ?? "";
  const expected = createHmac("sha256", process.env.HERCO_WEBHOOK_SECRET)
    .update(req.body) // raw body, not the parsed object
    .digest("hex");

  const a = Buffer.from(signature);
  const b = Buffer.from(expected);
  if (a.length !== b.length || !timingSafeEqual(a, b)) {
    return res.status(401).send("invalid signature");
  }

  const event = req.header("x-herco-event");
  const payload = JSON.parse(req.body.toString("utf8"));
  // handle the event, then answer 2xx quickly
  res.sendStatus(200);
});

Marketplace webhooks

The Hub also receives notifications from the platforms it is connected to. You do not need to call these — they are listed so your team knows the URLs configured on each provider.

POST /api/public/hooks/{shopify|mercadolivre|amazon|homedelivery}Provider notifications. Each provider's own signature is verified; the notification only schedules an incremental sync. Body limit 512 KB.
POST /api/public/hooks/syncInternal scheduler, runs every 10 minutes.
GET /api/public/auth/callbackOAuth return for marketplace connections.

Limits and best practices

Batch size
500 products, 500 inventory items, 200 shipments per request.
Read size
500 products, 200 orders per page.
Automatic sync
Runs every 10 minutes, up to 6 connections per cycle, with jitter and a 15-minute cooldown after a provider rate limit.
Inbound webhook body
512 KB maximum.
  • Use since on orders for incremental reads instead of full scans.
  • Send products and stock in batches instead of one request per SKU.
  • Retry 5xx responses with exponential backoff; results are idempotent by SKU and order reference.

Error codes

401 missing_api_keyNo Authorization: Bearer header was sent.
401 invalid_api_keyThe key does not exist or was revoked.
403 insufficient_scopeThe key lacks the scope required by the endpoint.
400 invalid_jsonThe request body could not be parsed.
413 payload_too_largeInbound webhook body above 512 KB.
500 / 503Temporary Hub failure — retry with backoff.

Bulk endpoints answer 200 with a per-item errors array: a partial batch is never silently discarded.