curl --request POST \
--url https://api.dev.shadowchef.co/v1/partner/menus/populate \
--header 'Authorization: Basic <encoded-value>' \
--header 'Content-Type: application/json' \
--data '
{
"stores": [
{
"brandID": 663,
"branchID": 1062
}
],
"sections": {
"1022": {
"sequence": 1,
"availableTimes": {
"0": {
"disabled": false,
"slots": [
{
"startTime": 200,
"endTime": 230
}
]
},
"1": {
"disabled": false,
"slots": [
{
"startTime": 200,
"endTime": 230
}
]
},
"2": {
"disabled": false,
"slots": [
{
"startTime": 200,
"endTime": 230
}
]
},
"3": {
"disabled": false,
"slots": [
{
"startTime": 200,
"endTime": 230
}
]
},
"4": {
"disabled": false,
"slots": [
{
"startTime": 200,
"endTime": 230
}
]
},
"5": {
"disabled": false,
"slots": [
{
"startTime": 200,
"endTime": 230
}
]
},
"6": {
"disabled": false,
"slots": [
{
"startTime": 200,
"endTime": 230
}
]
}
}
}
}
}
'const url = 'https://api.dev.shadowchef.co/v1/partner/menus/populate';
const options = {
method: 'POST',
headers: {Authorization: 'Basic <encoded-value>', 'Content-Type': 'application/json'},
body: JSON.stringify({
stores: [{brandID: 663, branchID: 1062}],
sections: {
'1022': {
sequence: 1,
availableTimes: {
'0': {disabled: false, slots: [{startTime: 200, endTime: 230}]},
'1': {disabled: false, slots: [{startTime: 200, endTime: 230}]},
'2': {disabled: false, slots: [{startTime: 200, endTime: 230}]},
'3': {disabled: false, slots: [{startTime: 200, endTime: 230}]},
'4': {disabled: false, slots: [{startTime: 200, endTime: 230}]},
'5': {disabled: false, slots: [{startTime: 200, endTime: 230}]},
'6': {disabled: false, slots: [{startTime: 200, endTime: 230}]}
}
}
}
})
};
fetch(url, options)
.then(res => res.json())
.then(json => console.log(json))
.catch(err => console.error(err));import requests
url = "https://api.dev.shadowchef.co/v1/partner/menus/populate"
payload = {
"stores": [
{
"brandID": 663,
"branchID": 1062
}
],
"sections": { "1022": {
"sequence": 1,
"availableTimes": {
"0": {
"disabled": False,
"slots": [
{
"startTime": 200,
"endTime": 230
}
]
},
"1": {
"disabled": False,
"slots": [
{
"startTime": 200,
"endTime": 230
}
]
},
"2": {
"disabled": False,
"slots": [
{
"startTime": 200,
"endTime": 230
}
]
},
"3": {
"disabled": False,
"slots": [
{
"startTime": 200,
"endTime": 230
}
]
},
"4": {
"disabled": False,
"slots": [
{
"startTime": 200,
"endTime": 230
}
]
},
"5": {
"disabled": False,
"slots": [
{
"startTime": 200,
"endTime": 230
}
]
},
"6": {
"disabled": False,
"slots": [
{
"startTime": 200,
"endTime": 230
}
]
}
}
} }
}
headers = {
"Authorization": "Basic <encoded-value>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.dev.shadowchef.co/v1/partner/menus/populate"
payload := strings.NewReader("{\n \"stores\": [\n {\n \"brandID\": 663,\n \"branchID\": 1062\n }\n ],\n \"sections\": {\n \"1022\": {\n \"sequence\": 1,\n \"availableTimes\": {\n \"0\": {\n \"disabled\": false,\n \"slots\": [\n {\n \"startTime\": 200,\n \"endTime\": 230\n }\n ]\n },\n \"1\": {\n \"disabled\": false,\n \"slots\": [\n {\n \"startTime\": 200,\n \"endTime\": 230\n }\n ]\n },\n \"2\": {\n \"disabled\": false,\n \"slots\": [\n {\n \"startTime\": 200,\n \"endTime\": 230\n }\n ]\n },\n \"3\": {\n \"disabled\": false,\n \"slots\": [\n {\n \"startTime\": 200,\n \"endTime\": 230\n }\n ]\n },\n \"4\": {\n \"disabled\": false,\n \"slots\": [\n {\n \"startTime\": 200,\n \"endTime\": 230\n }\n ]\n },\n \"5\": {\n \"disabled\": false,\n \"slots\": [\n {\n \"startTime\": 200,\n \"endTime\": 230\n }\n ]\n },\n \"6\": {\n \"disabled\": false,\n \"slots\": [\n {\n \"startTime\": 200,\n \"endTime\": 230\n }\n ]\n }\n }\n }\n }\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Basic <encoded-value>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.dev.shadowchef.co/v1/partner/menus/populate",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'stores' => [
[
'brandID' => 663,
'branchID' => 1062
]
],
'sections' => [
'1022' => [
'sequence' => 1,
'availableTimes' => [
'0' => [
'disabled' => false,
'slots' => [
[
'startTime' => 200,
'endTime' => 230
]
]
],
'1' => [
'disabled' => false,
'slots' => [
[
'startTime' => 200,
'endTime' => 230
]
]
],
'2' => [
'disabled' => false,
'slots' => [
[
'startTime' => 200,
'endTime' => 230
]
]
],
'3' => [
'disabled' => false,
'slots' => [
[
'startTime' => 200,
'endTime' => 230
]
]
],
'4' => [
'disabled' => false,
'slots' => [
[
'startTime' => 200,
'endTime' => 230
]
]
],
'5' => [
'disabled' => false,
'slots' => [
[
'startTime' => 200,
'endTime' => 230
]
]
],
'6' => [
'disabled' => false,
'slots' => [
[
'startTime' => 200,
'endTime' => 230
]
]
]
]
]
]
]),
CURLOPT_HTTPHEADER => [
"Authorization: Basic <encoded-value>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}{
"request_id": "req_4d1b7e3f-...",
"data": "<unknown>",
"error": {
"code": "auth_invalid_credential",
"message": "<string>",
"details": "<unknown>"
}
}Populate stores with the business-level menu (first-time push)
First-time-only push: stores that already have any menu rows
are skipped silently. To apply changes to an existing store
menu, use syncMenuToStores
instead.
curl --request POST \
--url https://api.dev.shadowchef.co/v1/partner/menus/populate \
--header 'Authorization: Basic <encoded-value>' \
--header 'Content-Type: application/json' \
--data '
{
"stores": [
{
"brandID": 663,
"branchID": 1062
}
],
"sections": {
"1022": {
"sequence": 1,
"availableTimes": {
"0": {
"disabled": false,
"slots": [
{
"startTime": 200,
"endTime": 230
}
]
},
"1": {
"disabled": false,
"slots": [
{
"startTime": 200,
"endTime": 230
}
]
},
"2": {
"disabled": false,
"slots": [
{
"startTime": 200,
"endTime": 230
}
]
},
"3": {
"disabled": false,
"slots": [
{
"startTime": 200,
"endTime": 230
}
]
},
"4": {
"disabled": false,
"slots": [
{
"startTime": 200,
"endTime": 230
}
]
},
"5": {
"disabled": false,
"slots": [
{
"startTime": 200,
"endTime": 230
}
]
},
"6": {
"disabled": false,
"slots": [
{
"startTime": 200,
"endTime": 230
}
]
}
}
}
}
}
'const url = 'https://api.dev.shadowchef.co/v1/partner/menus/populate';
const options = {
method: 'POST',
headers: {Authorization: 'Basic <encoded-value>', 'Content-Type': 'application/json'},
body: JSON.stringify({
stores: [{brandID: 663, branchID: 1062}],
sections: {
'1022': {
sequence: 1,
availableTimes: {
'0': {disabled: false, slots: [{startTime: 200, endTime: 230}]},
'1': {disabled: false, slots: [{startTime: 200, endTime: 230}]},
'2': {disabled: false, slots: [{startTime: 200, endTime: 230}]},
'3': {disabled: false, slots: [{startTime: 200, endTime: 230}]},
'4': {disabled: false, slots: [{startTime: 200, endTime: 230}]},
'5': {disabled: false, slots: [{startTime: 200, endTime: 230}]},
'6': {disabled: false, slots: [{startTime: 200, endTime: 230}]}
}
}
}
})
};
fetch(url, options)
.then(res => res.json())
.then(json => console.log(json))
.catch(err => console.error(err));import requests
url = "https://api.dev.shadowchef.co/v1/partner/menus/populate"
payload = {
"stores": [
{
"brandID": 663,
"branchID": 1062
}
],
"sections": { "1022": {
"sequence": 1,
"availableTimes": {
"0": {
"disabled": False,
"slots": [
{
"startTime": 200,
"endTime": 230
}
]
},
"1": {
"disabled": False,
"slots": [
{
"startTime": 200,
"endTime": 230
}
]
},
"2": {
"disabled": False,
"slots": [
{
"startTime": 200,
"endTime": 230
}
]
},
"3": {
"disabled": False,
"slots": [
{
"startTime": 200,
"endTime": 230
}
]
},
"4": {
"disabled": False,
"slots": [
{
"startTime": 200,
"endTime": 230
}
]
},
"5": {
"disabled": False,
"slots": [
{
"startTime": 200,
"endTime": 230
}
]
},
"6": {
"disabled": False,
"slots": [
{
"startTime": 200,
"endTime": 230
}
]
}
}
} }
}
headers = {
"Authorization": "Basic <encoded-value>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.dev.shadowchef.co/v1/partner/menus/populate"
payload := strings.NewReader("{\n \"stores\": [\n {\n \"brandID\": 663,\n \"branchID\": 1062\n }\n ],\n \"sections\": {\n \"1022\": {\n \"sequence\": 1,\n \"availableTimes\": {\n \"0\": {\n \"disabled\": false,\n \"slots\": [\n {\n \"startTime\": 200,\n \"endTime\": 230\n }\n ]\n },\n \"1\": {\n \"disabled\": false,\n \"slots\": [\n {\n \"startTime\": 200,\n \"endTime\": 230\n }\n ]\n },\n \"2\": {\n \"disabled\": false,\n \"slots\": [\n {\n \"startTime\": 200,\n \"endTime\": 230\n }\n ]\n },\n \"3\": {\n \"disabled\": false,\n \"slots\": [\n {\n \"startTime\": 200,\n \"endTime\": 230\n }\n ]\n },\n \"4\": {\n \"disabled\": false,\n \"slots\": [\n {\n \"startTime\": 200,\n \"endTime\": 230\n }\n ]\n },\n \"5\": {\n \"disabled\": false,\n \"slots\": [\n {\n \"startTime\": 200,\n \"endTime\": 230\n }\n ]\n },\n \"6\": {\n \"disabled\": false,\n \"slots\": [\n {\n \"startTime\": 200,\n \"endTime\": 230\n }\n ]\n }\n }\n }\n }\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Basic <encoded-value>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.dev.shadowchef.co/v1/partner/menus/populate",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'stores' => [
[
'brandID' => 663,
'branchID' => 1062
]
],
'sections' => [
'1022' => [
'sequence' => 1,
'availableTimes' => [
'0' => [
'disabled' => false,
'slots' => [
[
'startTime' => 200,
'endTime' => 230
]
]
],
'1' => [
'disabled' => false,
'slots' => [
[
'startTime' => 200,
'endTime' => 230
]
]
],
'2' => [
'disabled' => false,
'slots' => [
[
'startTime' => 200,
'endTime' => 230
]
]
],
'3' => [
'disabled' => false,
'slots' => [
[
'startTime' => 200,
'endTime' => 230
]
]
],
'4' => [
'disabled' => false,
'slots' => [
[
'startTime' => 200,
'endTime' => 230
]
]
],
'5' => [
'disabled' => false,
'slots' => [
[
'startTime' => 200,
'endTime' => 230
]
]
],
'6' => [
'disabled' => false,
'slots' => [
[
'startTime' => 200,
'endTime' => 230
]
]
]
]
]
]
]),
CURLOPT_HTTPHEADER => [
"Authorization: Basic <encoded-value>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}{
"request_id": "req_4d1b7e3f-...",
"data": "<unknown>",
"error": {
"code": "auth_invalid_credential",
"message": "<string>",
"details": "<unknown>"
}
}Authorizations
Authorization: Basic base64(partner_key:secret_key).
Credentials are issued by a klikit operator. The plaintext
secret_key is shown once at issuance and cannot be retrieved
later — store it securely. If lost, ask your operator to rotate
the secret to receive a new one. The old secret stops working
immediately on rotation; there is no overlap window.
Body
Push the named sections to each (brand, branch) store.
Two read-this-first behaviours:
-
Overlap check. A non-empty
sections[<id>].availableTimestriggers an overlap check against schedules already attached to the store; a collision returns400 request_invalid. The simplest opt-out is to sendavailableTimes: {}— the menu service then falls back to the section's business-level schedule. -
Persisted fields. Sync deliberately keeps the store's existing overrides for a few fields (defaults listed under
PersistFieldOnSync) — so a stock store-only schedule edit isn't blown away when you ship an unrelated business-level change. To overwrite one of those fields from the business-level value, include its name infieldIncludeOnSync. Example:{ "fieldIncludeOnSync": ["availableTimes"] }subtracts
availableTimesfrom the persist list so the store-section schedule is overwritten with the section's current business-levelavailableTimes.
After sync, the partner-facing getMenu response reads from a
cached blob — call publishMenu
afterwards to rebuild it (and to ship the change to provider
marketplaces).
Show child attributes
Show child attributes
Section-id keyed object. Each value carries display order + per-store schedule overrides.
Show child attributes
Show child attributes
Field names to exclude from the persist list — i.e. force-overwrite
the store's existing override with the business-level value. Common
picks: availableTimes, isEnabled, visibilities, blackoutDates
for sections.
Explicit per-level persist allowlist. If you set this, the menu
service replaces the default list wholesale — usually you want to
leave this unset and use fieldIncludeOnSync instead.
Show child attributes
Show child attributes
Response
Success — payload wrapped in the canonical Envelope.
Canonical response wrapper. Every response — success or error —
carries the request_id so you can quote one id to klikit
support to correlate a request end-to-end.
"req_4d1b7e3f-..."
Endpoint-specific payload on success.
Machine-readable error code + human message. The code is stable
across releases — switch on code in your client code rather
than parsing the message text.
Common codes you will encounter as a partner:
| Code | HTTP | Meaning |
|---|---|---|
auth_missing | 401 | Authorization header absent / malformed |
auth_invalid_credential | 401 | partner_key or secret_key did not verify |
auth_revoked | 403 | Credential is revoked |
auth_forbidden | 403 | Credential not authorized for the requested scope |
request_invalid | 400 | Body / query parameters failed validation |
request_missing_idempotency_key | 400 | Write endpoint called without Idempotency-Key |
request_invalid_range | 400 | Date range > 90 days |
resource_not_found | 404 | Order / store / mapping does not exist |
resource_unmapped | 404 | Stock / availability call referenced an unknown SKU |
state_invalid_transition | 409 | Order PATCH not allowed by current state |
state_idempotency_conflict | 409 | Same Idempotency-Key reused with a different body |
rate_limit_exceeded | 429 | Per-credential rate cap hit |
downstream_unavailable | 502 | An internal klikit dependency is unreachable |
Show child attributes
Show child attributes