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.comFormat
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.
Authorization: Bearer hk_live_xxxxxxxxxxxxxxxxxxxxxxxx
Content-Type: application/jsonScopes
| products:read | Read catalog and inventory. |
| products:write | Create and update products. |
| orders:read | Read orders and post shipment updates. |
| inventory:write | Push stock quantities. |
Endpoints
/api/public/v1/productsproducts:readList 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"{
"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"
}
]
}/api/public/v1/productsproducts:writeCreate 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 } ] }'{ "imported": 1, "rejected": 0, "errors": [] }| invalid_json | Body is not valid JSON. |
| expected_products_array | Send an array, or an object with a products array. |
| batch_too_large | More than 500 rows in one call. |
/api/public/v1/ordersorders:readMarketplace orders with their items.
Sorted by order date, newest first. Use since for incremental reads instead of full scans.
Query parameters
| since | ISO 8601 | Only orders placed at or after this timestamp. |
| status | string | Filter by order status. |
| limit | number | Page size. Default 100, maximum 200. |
curl "https://hercohub.com/api/public/v1/orders?limit=50" \
-H "Authorization: Bearer hk_live_YOUR_KEY"{
"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 }
]
}
]
}/api/public/v1/inventoryproducts:readStock per product and per location.
curl "https://hercohub.com/api/public/v1/inventory" \
-H "Authorization: Bearer hk_live_YOUR_KEY"{
"data": [
{
"sku": "ABC-1",
"stock": 120,
"inventory_levels": [
{ "quantity": 120, "location": { "name": "Miami DC", "kind": "us_warehouse" } }
]
}
]
}/api/public/v1/inventoryinventory:writePush 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" } ] }'{
"updated": 1,
"rejected": 1,
"errors": [ { "sku": "XYZ-9", "message": "unknown_sku" } ]
}| sku_required | Item sent without a SKU. |
| unknown_sku | SKU does not exist in this company's catalog. |
| no_location_configured | No matching inventory location is configured. |
/api/public/v1/shipmentsorders:readSend 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" } ] }'{ "updated": 1, "rejected": 0, "errors": [] }| order_reference_required | Neither external_order_id nor order_id was sent. |
| order_not_found | No 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.
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.created | A new marketplace order arrived in the Hub. |
| order.status_changed | Order status, tracking code or carrier changed. |
| shipment.created | A shipping label was generated for an order. |
| inventory.updated | Stock 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/sync | Internal scheduler, runs every 10 minutes. |
| GET /api/public/auth/callback | OAuth 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_key | No Authorization: Bearer header was sent. |
| 401 invalid_api_key | The key does not exist or was revoked. |
| 403 insufficient_scope | The key lacks the scope required by the endpoint. |
| 400 invalid_json | The request body could not be parsed. |
| 413 payload_too_large | Inbound webhook body above 512 KB. |
| 500 / 503 | Temporary Hub failure — retry with backoff. |
Bulk endpoints answer 200 with a per-item errors array: a partial batch is never silently discarded.