Reference data
http
GET /v1/referenceOne call that tells an integration every allowed value and this
venue's own limits — fetch it first and you never have to guess an enum
or discover a cap through a 400. Available on every plan with an active
subscription.
Response#
json
{
"units": ["PCS", "G", "ML"],
"badgeColors": ["RED", "PURPLE", "YELLOW", "GREEN", "BLUE", "GREY"],
"modifierTypes": {
"accepted": ["single_choice", "multiple_choice", "add_ingredients", "remove_ingredients"],
"canonical": ["single_choice", "add_ingredients", "remove_ingredients"],
"deprecated": {
"multiple_choice": "accepted for backward compatibility, stored as 'add_ingredients'"
}
},
"orderStatuses": ["new", "confirmed", "preparing", "delivering", "completed", "cancelled"],
"orderStatusTransitions": {
"new": ["confirmed", "cancelled"],
"confirmed": ["preparing", "cancelled"],
"preparing": ["delivering", "completed", "cancelled"],
"delivering": ["completed", "cancelled"],
"completed": [],
"cancelled": []
},
"cardStyles": ["minimalism", "compact", "premium"],
"currencies": ["UAH", "USD", "EUR", "PLN", "GBP", "CAD", "PEN", "CLP", "MXN", "COP", "UZS"],
"languages": ["uk", "en", "es", "fr", "de", "it", "pt-BR", "ru", "pl", "cs", "zh-CN", "ar", "ja", "uz"],
"requestLimits": {
"bodyLimitMb": 5,
"mediaBodyLimitMb": 21,
"rateLimits": {
"short": { "limit": 10, "windowSec": 1 },
"medium": { "limit": 50, "windowSec": 10 },
"long": { "limit": 300, "windowSec": 60 }
},
"planRateLimits": {
"windowSec": 60,
"keyedBy": "venue",
"tiers": {
"general": { "free": 30, "paid": 120 },
"media": { "free": 5, "paid": 30 },
"heavy": { "free": 10, "paid": 60 }
}
},
"syncArrayCaps": { "categories": 200, "ingredients": 200, "products": 500 },
"syncNestedCaps": {
"modifierGroupsPerProduct": 20,
"optionsPerModifierGroup": 50,
"ingredientRefsPerProduct": 100
},
"cleanupArrayCap": 1000
},
"conventions": {
"money": "All amounts are integers in minor currency units (kopecks/cents)",
"timestamps": "ISO 8601 with an explicit timezone (UTC recommended)"
},
"planLimits": {
"plan": "duckling",
"rateLimitTier": "paid",
"limits": {
"maxProducts": 2000,
"maxCategories": 200,
"maxIngredients": 300,
"maxBanners": 20,
"maxQrTables": 500
},
"usage": {
"products": 64,
"categories": 8,
"ingredients": 41,
"banners": 2,
"tables": 12
},
"features": {
"hasDelivery": true,
"aiEnabled": true,
"ordersApi": true
}
}
}Everything above planLimits is static catalog data served straight from
the same definitions the API validates against — the values can't drift
from what endpoints accept. planLimits reflects your venue's current
plan and updates immediately on plan changes.
How an agent should use it#
- Fetch once per session, before writing.
- Validate enum inputs (
unit,badgeColor, modifiertype,cardStyle,defaultCurrency, locales) against the catalog instead of hardcoding. - Check headroom before bulk syncs directly:
planLimits.limits.maxProducts − planLimits.usage.products— no row fetch needed (usageuses the same filters as the limit checks). - Branch features:
features.hasDeliverybefore touching order-config settings,features.ordersApibefore the Orders API. - Pace requests from
requestLimits.planRateLimits+planLimits.rateLimitTier— your venue's actual rate-limit budgets — instead of hardcoding them.
Example#
bash
curl https://api.duck-hub.com/v1/reference \
-H "Authorization: Bearer dk_live_your_api_key"javascript
const response = await fetch('https://api.duck-hub.com/v1/reference', {
headers: { Authorization: 'Bearer dk_live_your_api_key' },
})
const ref = await response.json()
if (!ref.planLimits.features.hasDelivery) {
// skip delivery configuration for this venue
}