O que você pode fazer
- Exibir seu cardápio no seu próprio site ou app. Um
GET público retorna o cardápio completo em JSON, pronto para você renderizar como quiser. Não precisa de chave. - Gerenciar o cardápio a partir do seu sistema. Categorias, produtos, preços, disponibilidade, variantes, fotos, logo e banner: o mesmo que o painel, por HTTP e com uma chave de API.
Se o que você quer é que um agente de IA gerencie o cardápio conversando, isso é o servidor MCP (https://delimenu.co/api/mcp), que usa as mesmas operações.
Comece em três passos
- No painel, acesse Configurações › API para desenvolvedores e clique em Criar chave. Escolha um nome e as permissões: ver o cardápio, ou ver e editar.
- Copie a chave quando ela aparecer. Ela começa com
dmk_ e só é exibida uma vez; se você a perder, revogue-a e crie outra. - Envie-a em cada requisição no cabeçalho
Authorization:
curl https://delimenu.co/api/v1/restaurants \
-H "Authorization: Bearer dmk_…"
A URL base é https://delimenu.co/api/v1. As respostas são JSON em snake_case. Cada conta pode ter até 10 chaves ativas, e uma chave vale para todos os restaurantes da conta.
Seu cardápio no seu próprio frontend
O cardápio público de qualquer restaurante está em https://delimenu.co/api/v1/menus/{identifier}, onde identifier é o mesmo que aparece em https://delimenu.co/{identifier}. Ele retorna exatamente o que um cliente vê: as categorias com pelo menos um produto visível, em ordem, e seus produtos com preços, promoções, disponibilidade, variantes e fotos.
const res = await fetch('https://delimenu.co/api/v1/menus/demo')
const { restaurant, categories } = await res.json()
for (const category of categories) {
console.log(category.name)
for (const product of category.products) {
console.log(' ', product.name, product.price, restaurant.currency)
}
}
Pode ser chamado diretamente do navegador (CORS aberto) e fica em cache na borda: cada alteração que você fizer no painel, pela API ou com um agente o atualiza em segundos. Se o restaurante não tiver um plano ativo, responde 403 apenas com o nome dele, assim como a página pública.
Exemplos completos. Dois sites em Next.js que leem um cardápio real com essa única chamada e o exibem com um design próprio: Maison (código) e Qitchen (código). Copie o que preferir e troque o identificador pelo seu.
Webhooks: fique sabendo de cada alteração
Em vez de consultar a API a todo momento, registre uma URL e o Delimenu enviará a ela um POST sempre que o cardápio mudar: ao criar, editar ou excluir um produto ou uma categoria, e ao alterar os dados do restaurante. Eles são criados em Configurações › Webhooks no painel ou com POST /restaurants/{ref}/webhooks, até 5 por restaurante. Eventos disponíveis: product.created, product.updated, product.deleted, category.created, category.updated, category.deleted, restaurant.updated, restaurant.deleted.
Cada requisição vem assinada no cabeçalho Delimenu-Signature com o segredo do endpoint, que é exibido uma única vez. Verifique-a sempre, sobre o corpo exatamente como chegou:
import { createHmac, timingSafeEqual } from 'node:crypto'
// rawBody: the request body exactly as received (a string or Buffer), not re-serialized JSON
export function verifyDelimenuWebhook(rawBody, header, secret) {
const parts = Object.fromEntries(header.split(',').map(part => part.split('=')))
const timestamp = Number(parts.t)
if (!timestamp || Math.abs(Date.now() / 1000 - timestamp) > 300) return false
const expected = createHmac('sha256', secret).update(`${timestamp}.${rawBody}`).digest('hex')
const received = Buffer.from(parts.v1 ?? '', 'hex')
return received.length === 32 && timingSafeEqual(received, Buffer.from(expected, 'hex'))
}
// const ok = verifyDelimenuWebhook(rawBody, req.headers['delimenu-signature'], process.env.DELIMENU_WEBHOOK_SECRET)
Responda com qualquer 2xx em menos de 10 segundos e faça o trabalho depois. Se falhar, tentamos de novo com espera crescente até 8 vezes durante cerca de um dia, então use o id do evento para não processá-lo duas vezes. Um endpoint que esgota as tentativas em 20 eventos seguidos é desativado; você o reativa pelo painel.
O que vale saber
- Os preços são unidades inteiras da moeda do restaurante, nunca centavos:
12500 são 12.500 pesos, 5.99 são US$ 5,99. A moeda vem em restaurant.currency. - As alterações são imediatas e aparecem no cardápio público como se você as tivesse feito pelo painel. Não há rascunho nem botão de publicar.
- Um restaurante sem plano ativo (assinatura ou teste) responde
402 em todos os endpoints com chave, assim como o painel exibe o aviso de assinatura. - As permissões são por chave. Uma chave somente leitura recebe
403 em qualquer escrita, com o cabeçalho WWW-Authenticate dizendo qual permissão falta. - Editar muitos produtos de uma vez é feito em uma única requisição, até 50 por chamada.
- As imagens são enviadas por URL pública (JPG, PNG ou WEBP, até 5 MB). O Delimenu faz o download, otimiza e responde quando a foto já está pronta, então essas chamadas levam alguns segundos.
- Revogar uma chave no painel corta o acesso na requisição seguinte.
Para agentes de IA e ferramentas
- Esta mesma documentação em Markdown, com a referência e um exemplo por endpoint, está em /llms.txt. É o que convém entregar ao Claude Code, ao Cursor ou ao ChatGPT.
- A descrição formal está em OpenAPI 3.0, com
operationId e exemplos de requisição e resposta em cada operação, pronta para gerar um cliente ou importá-la na sua ferramenta. - Se você quer que um agente edite o cardápio conversando, sem programar nada, use o servidor MCP: mesmas operações, autorização com OAuth a partir do próprio agente.
Erros
Todo erro responde { "error", "message" }: error é um código estável em inglês para o seu programa decidir, e message é um texto em espanhol que você pode exibir como está. Os 401 e 403 acrescentam docs com a URL desta página, para quem chegar à API sem tê-la lido.
| HTTP | error | Quando |
|---|
400 | VALIDATION | Falta um campo ou ele tem um formato inválido. `issues` diz qual. |
401 | UNAUTHORIZED | A chave está ausente, não existe ou foi revogada. |
402 | NOT_PREMIUM | O restaurante não tem assinatura nem teste ativo. |
403 | FORBIDDEN | A chave não tem a permissão que o endpoint exige. |
404 | NOT_FOUND | O restaurante não está na sua conta, ou o produto ou a categoria não existem nele. |
409 | CONFLICT | Nome de categoria repetido, identificador já em uso ou categoria com produtos. |
422 | LIMIT | Um limite foi atingido. |
500 | INTERNAL | Algo falhou do nosso lado. Tente novamente em instantes. |
Referência
Cada operação traz seu curl e sua resposta de exemplo; as chaves e os ids dos exemplos são fictícios. Os campos marcados com * são obrigatórios.
Public menu
No key needed. What delimenu.co/{identifier} shows.
Restaurants
The restaurants of the account the key belongs to.
get/restaurantsrequer menu:readlistRestaurants
List the restaurants of the account
Call this first; every other endpoint takes one of these by identifier or id.
Exemplo
curl -X GET "https://delimenu.co/api/v1/restaurants" \
-H "Authorization: Bearer dmk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
Resposta 200
{
"restaurants": [
{
"id": "aR3kX9pLm2QzT7vN4bYc",
"identifier": "pizzeria-roma",
"name": "Pizzería Roma",
"currency": "COP",
"type": "whatsapp",
"phone": "+573001234567",
"address": "Calle 10 # 5-20, Bogotá",
"menu_url": "https://delimenu.co/pizzeria-roma",
"logo_url": "https://firebasestorage.googleapis.com/v0/b/example/o/logo_512x512.png?alt=media",
"banner_url": null,
"language": "es",
"languages": [
"en"
],
"trial_active": false
}
]
}
Campos da resposta
| Campo | Tipo | Descrição |
|---|
restaurants* | object[] | |
id* | string | |
identifier* | string | |
name* | string | |
currency* | string | ISO 4217 code every price is in |
type* | "whatsapp" | "read_only" | "whatsapp" takes orders by WhatsApp; "read_only" only shows the menu |
phone* | string | null | WhatsApp number in E.164, when ordering is enabled |
address* | string | null | |
menu_url* | string | |
logo_url* | string | null | |
banner_url* | string | null | |
language* | "es" | "en" | "pt" | "fr" | "de" | "it" | The primary language: the one name, description and every untranslated field are written in |
languages* | "es" | "en" | "pt" | "fr" | "de" | "it"[] | The additional languages diners can read the menu in, translated through `translations`. Empty for a menu in one language. |
trial_active* | boolean | |
Erros
401 UNAUTHORIZED — the key is missing, unknown or revoked402 NOT_PREMIUM — the restaurant has no active subscription or trial403 FORBIDDEN — the key lacks the scope this endpoint needs404 NOT_FOUND — no such restaurant in this account, or no such item in it500 INTERNAL — something failed on our side; retry in a moment
get/restaurants/{ref}requer menu:readgetRestaurant
Get a restaurant
Parâmetros
| Nome | Onde | Descrição |
|---|
ref* | caminho | The restaurant identifier (the slug in its public URL, e.g. "pizzeria-roma") or its id, as returned by GET /restaurants |
Exemplo
curl -X GET "https://delimenu.co/api/v1/restaurants/pizzeria-roma" \
-H "Authorization: Bearer dmk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
Resposta 200
{
"restaurant": {
"id": "aR3kX9pLm2QzT7vN4bYc",
"identifier": "pizzeria-roma",
"name": "Pizzería Roma",
"currency": "COP",
"type": "whatsapp",
"phone": "+573001234567",
"address": "Calle 10 # 5-20, Bogotá",
"menu_url": "https://delimenu.co/pizzeria-roma",
"logo_url": "https://firebasestorage.googleapis.com/v0/b/example/o/logo_512x512.png?alt=media",
"banner_url": null,
"language": "es",
"languages": [
"en"
],
"trial_active": false
}
}
Campos da resposta
| Campo | Tipo | Descrição |
|---|
restaurant* | object | |
id* | string | |
identifier* | string | |
name* | string | |
currency* | string | ISO 4217 code every price is in |
type* | "whatsapp" | "read_only" | "whatsapp" takes orders by WhatsApp; "read_only" only shows the menu |
phone* | string | null | WhatsApp number in E.164, when ordering is enabled |
address* | string | null | |
menu_url* | string | |
logo_url* | string | null | |
banner_url* | string | null | |
language* | "es" | "en" | "pt" | "fr" | "de" | "it" | The primary language: the one name, description and every untranslated field are written in |
languages* | "es" | "en" | "pt" | "fr" | "de" | "it"[] | The additional languages diners can read the menu in, translated through `translations`. Empty for a menu in one language. |
trial_active* | boolean | |
Erros
401 UNAUTHORIZED — the key is missing, unknown or revoked402 NOT_PREMIUM — the restaurant has no active subscription or trial403 FORBIDDEN — the key lacks the scope this endpoint needs404 NOT_FOUND — no such restaurant in this account, or no such item in it500 INTERNAL — something failed on our side; retry in a moment
patch/restaurants/{ref}requer menu:writeupdateRestaurant
Update a restaurant
Changes only the fields given: name, WhatsApp phone, address, currency, type, language (the primary one) or languages (the additional ones, never the primary). Changing the currency converts no prices and changing a language translates nothing. The identifier has its own endpoint.
Parâmetros
| Nome | Onde | Descrição |
|---|
ref* | caminho | The restaurant identifier (the slug in its public URL, e.g. "pizzeria-roma") or its id, as returned by GET /restaurants |
Corpo (JSON)
| Campo | Tipo | Descrição |
|---|
name | string | até 50 caracteres |
phone | string | WhatsApp number that receives orders, in E.164 format (+573001234567) |
address | string | Street address shown on the menu. Empty string removes it.até 65 caracteres |
currency | string | ISO 4217 code every price is shown in. Changing it converts nothing. |
type | "whatsapp" | "read_only" | "whatsapp" takes orders by WhatsApp; "read_only" only shows the menu |
language | "es" | "en" | "pt" | "fr" | "de" | "it" | The primary language of the menu: the one its names and descriptions are written in, and the one WhatsApp orders are composed in. Changing it translates nothing. If the new one was among `languages`, it is taken out of that list. |
languages | "es" | "en" | "pt" | "fr" | "de" | "it"[] | Up to 3 additional languages diners can read the menu in, translated by hand through `translations`. Never the primary language. Replaces the whole list; [] leaves the menu in one language. |
Exemplo
curl -X PATCH "https://delimenu.co/api/v1/restaurants/pizzeria-roma" \
-H "Authorization: Bearer dmk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{"phone":"+573009876543","address":"Carrera 7 # 45-10, Bogotá"}'
Resposta 200
{
"restaurant": {
"id": "aR3kX9pLm2QzT7vN4bYc",
"identifier": "pizzeria-roma",
"name": "Pizzería Roma",
"currency": "COP",
"type": "whatsapp",
"phone": "+573009876543",
"address": "Carrera 7 # 45-10, Bogotá",
"menu_url": "https://delimenu.co/pizzeria-roma",
"logo_url": "https://firebasestorage.googleapis.com/v0/b/example/o/logo_512x512.png?alt=media",
"banner_url": null,
"language": "es",
"languages": [
"en"
],
"trial_active": false
}
}
Campos da resposta
| Campo | Tipo | Descrição |
|---|
restaurant* | object | |
id* | string | |
identifier* | string | |
name* | string | |
currency* | string | ISO 4217 code every price is in |
type* | "whatsapp" | "read_only" | "whatsapp" takes orders by WhatsApp; "read_only" only shows the menu |
phone* | string | null | WhatsApp number in E.164, when ordering is enabled |
address* | string | null | |
menu_url* | string | |
logo_url* | string | null | |
banner_url* | string | null | |
language* | "es" | "en" | "pt" | "fr" | "de" | "it" | The primary language: the one name, description and every untranslated field are written in |
languages* | "es" | "en" | "pt" | "fr" | "de" | "it"[] | The additional languages diners can read the menu in, translated through `translations`. Empty for a menu in one language. |
trial_active* | boolean | |
Erros
400 VALIDATION — a field is missing or malformed; `issues` names it401 UNAUTHORIZED — the key is missing, unknown or revoked402 NOT_PREMIUM — the restaurant has no active subscription or trial403 FORBIDDEN — the key lacks the scope this endpoint needs404 NOT_FOUND — no such restaurant in this account, or no such item in it500 INTERNAL — something failed on our side; retry in a moment
put/restaurants/{ref}/identifierrequer menu:writeupdateRestaurantIdentifier
Change the public URL
Changes the slug of the menu (delimenu.co/{identifier}). The old URL stops working immediately; printed QR codes keep working because they point at the restaurant id.
Parâmetros
| Nome | Onde | Descrição |
|---|
ref* | caminho | The restaurant identifier (the slug in its public URL, e.g. "pizzeria-roma") or its id, as returned by GET /restaurants |
Corpo (JSON)
| Campo | Tipo | Descrição |
|---|
identifier* | string | The new public slug: lowercase letters, digits, dots, hyphens and underscores, e.g. "pizzeria-roma"até 50 caracteres |
Exemplo
curl -X PUT "https://delimenu.co/api/v1/restaurants/pizzeria-roma/identifier" \
-H "Authorization: Bearer dmk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{"identifier":"pizzeria-roma-chapinero"}'
Resposta 200
{
"restaurant": {
"id": "aR3kX9pLm2QzT7vN4bYc",
"identifier": "pizzeria-roma-chapinero",
"name": "Pizzería Roma",
"currency": "COP",
"type": "whatsapp",
"phone": "+573001234567",
"address": "Calle 10 # 5-20, Bogotá",
"menu_url": "https://delimenu.co/pizzeria-roma-chapinero",
"logo_url": "https://firebasestorage.googleapis.com/v0/b/example/o/logo_512x512.png?alt=media",
"banner_url": null,
"language": "es",
"languages": [
"en"
],
"trial_active": false
}
}
Campos da resposta
| Campo | Tipo | Descrição |
|---|
restaurant* | object | |
id* | string | |
identifier* | string | |
name* | string | |
currency* | string | ISO 4217 code every price is in |
type* | "whatsapp" | "read_only" | "whatsapp" takes orders by WhatsApp; "read_only" only shows the menu |
phone* | string | null | WhatsApp number in E.164, when ordering is enabled |
address* | string | null | |
menu_url* | string | |
logo_url* | string | null | |
banner_url* | string | null | |
language* | "es" | "en" | "pt" | "fr" | "de" | "it" | The primary language: the one name, description and every untranslated field are written in |
languages* | "es" | "en" | "pt" | "fr" | "de" | "it"[] | The additional languages diners can read the menu in, translated through `translations`. Empty for a menu in one language. |
trial_active* | boolean | |
Erros
400 VALIDATION — a field is missing or malformed; `issues` names it401 UNAUTHORIZED — the key is missing, unknown or revoked402 NOT_PREMIUM — the restaurant has no active subscription or trial403 FORBIDDEN — the key lacks the scope this endpoint needs404 NOT_FOUND — no such restaurant in this account, or no such item in it409 CONFLICT — the identifier is already taken500 INTERNAL — something failed on our side; retry in a moment
Categories
get/restaurants/{ref}/categoriesrequer menu:readlistCategories
List the categories
Parâmetros
| Nome | Onde | Descrição |
|---|
ref* | caminho | The restaurant identifier (the slug in its public URL, e.g. "pizzeria-roma") or its id, as returned by GET /restaurants |
Exemplo
curl -X GET "https://delimenu.co/api/v1/restaurants/pizzeria-roma/categories" \
-H "Authorization: Bearer dmk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
Resposta 200
{
"categories": [
{
"id": "k3Qm8vXb2LpN7wRt1YaZ",
"name": "Pizzas",
"position": 1
},
{
"id": "c7Wd2Bn5Kq9Xr4Ms8Lt1",
"name": "Bebidas",
"position": 2
}
]
}
Campos da resposta
| Campo | Tipo | Descrição |
|---|
categories* | object[] | |
id* | string | |
name* | string | |
translations | object | The name in other languages, keyed by language code, e.g. { "en": { "name": "Large" } }. Absent when nothing is translated; a language missing here falls back to `name`. |
es | object | |
name | string | |
en | object | |
name | string | |
pt | object | |
name | string | |
fr | object | |
name | string | |
de | object | |
name | string | |
it | object | |
name | string | |
position* | integer | min -9007199254740991 · max 9007199254740991 |
Erros
401 UNAUTHORIZED — the key is missing, unknown or revoked402 NOT_PREMIUM — the restaurant has no active subscription or trial403 FORBIDDEN — the key lacks the scope this endpoint needs404 NOT_FOUND — no such restaurant in this account, or no such item in it500 INTERNAL — something failed on our side; retry in a moment
post/restaurants/{ref}/categoriesrequer menu:writecreateCategory
Create a category
Adds a category at the end of the menu. name is in the menu's primary language; translations carries it in the others.
Parâmetros
| Nome | Onde | Descrição |
|---|
ref* | caminho | The restaurant identifier (the slug in its public URL, e.g. "pizzeria-roma") or its id, as returned by GET /restaurants |
Corpo (JSON)
| Campo | Tipo | Descrição |
|---|
name* | string | Category nameaté 50 caracteres |
translations | object | The category name in the menu's other languages, e.g. { "en": { "name": "Drinks" } }. Keyed by language code (es, en, pt, fr, de, it). Shown to diners who read the menu in that language; a language without a translation falls back to the original text, and an entry in the menu's primary language is ignored. Replaces the whole map when provided; {} removes every translation. |
es | object | |
name | string | The name in that languageaté 50 caracteres |
en | object | |
name | string | The name in that languageaté 50 caracteres |
pt | object | |
name | string | The name in that languageaté 50 caracteres |
fr | object | |
name | string | The name in that languageaté 50 caracteres |
de | object | |
name | string | The name in that languageaté 50 caracteres |
it | object | |
name | string | The name in that languageaté 50 caracteres |
Exemplo
curl -X POST "https://delimenu.co/api/v1/restaurants/pizzeria-roma/categories" \
-H "Authorization: Bearer dmk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{"name":"Postres","translations":{"en":{"name":"Desserts"}}}'
Resposta 201
{
"category": {
"id": "d4Fg6Hj8Kl0Zx2Cv4Bn6",
"name": "Postres",
"translations": {
"en": {
"name": "Desserts"
}
}
}
}
Campos da resposta
| Campo | Tipo | Descrição |
|---|
category* | object | |
id* | string | |
name* | string | |
translations | object | The name in other languages, keyed by language code, e.g. { "en": { "name": "Large" } }. Absent when nothing is translated; a language missing here falls back to `name`. |
es | object | |
name | string | |
en | object | |
name | string | |
pt | object | |
name | string | |
fr | object | |
name | string | |
de | object | |
name | string | |
it | object | |
name | string | |
Erros
400 VALIDATION — a field is missing or malformed; `issues` names it401 UNAUTHORIZED — the key is missing, unknown or revoked402 NOT_PREMIUM — the restaurant has no active subscription or trial403 FORBIDDEN — the key lacks the scope this endpoint needs404 NOT_FOUND — no such restaurant in this account, or no such item in it409 CONFLICT — a category with that name already exists500 INTERNAL — something failed on our side; retry in a moment
put/restaurants/{ref}/category-orderrequer menu:writereorderCategories
Reorder the categories
Sets the display order. Pass every category id exactly once, in the new order.
Parâmetros
| Nome | Onde | Descrição |
|---|
ref* | caminho | The restaurant identifier (the slug in its public URL, e.g. "pizzeria-roma") or its id, as returned by GET /restaurants |
Corpo (JSON)
| Campo | Tipo | Descrição |
|---|
category_ids* | string[] | Every category id exactly once, in the new display order |
Exemplo
curl -X PUT "https://delimenu.co/api/v1/restaurants/pizzeria-roma/category-order" \
-H "Authorization: Bearer dmk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{"category_ids":["c7Wd2Bn5Kq9Xr4Ms8Lt1","k3Qm8vXb2LpN7wRt1YaZ"]}'
Resposta 200
{
"order_categories": [
"c7Wd2Bn5Kq9Xr4Ms8Lt1",
"k3Qm8vXb2LpN7wRt1YaZ"
]
}
Campos da resposta
| Campo | Tipo | Descrição |
|---|
order_categories* | string[] | |
Erros
400 VALIDATION — a field is missing or malformed; `issues` names it401 UNAUTHORIZED — the key is missing, unknown or revoked402 NOT_PREMIUM — the restaurant has no active subscription or trial403 FORBIDDEN — the key lacks the scope this endpoint needs404 NOT_FOUND — no such restaurant in this account, or no such item in it500 INTERNAL — something failed on our side; retry in a moment
patch/restaurants/{ref}/categories/{id}requer menu:writeupdateCategory
Rename or translate a category
Pass name, translations or both; what is not passed is left as it is. translations replaces the whole map, and {} removes every translation.
Parâmetros
| Nome | Onde | Descrição |
|---|
ref* | caminho | The restaurant identifier (the slug in its public URL, e.g. "pizzeria-roma") or its id, as returned by GET /restaurants |
id* | caminho | The category id, from GET /restaurants/{ref}/categories |
Corpo (JSON)
| Campo | Tipo | Descrição |
|---|
name | string | Category nameaté 50 caracteres |
translations | object | The category name in the menu's other languages, e.g. { "en": { "name": "Drinks" } }. Keyed by language code (es, en, pt, fr, de, it). Shown to diners who read the menu in that language; a language without a translation falls back to the original text, and an entry in the menu's primary language is ignored. Replaces the whole map when provided; {} removes every translation. |
es | object | |
name | string | The name in that languageaté 50 caracteres |
en | object | |
name | string | The name in that languageaté 50 caracteres |
pt | object | |
name | string | The name in that languageaté 50 caracteres |
fr | object | |
name | string | The name in that languageaté 50 caracteres |
de | object | |
name | string | The name in that languageaté 50 caracteres |
it | object | |
name | string | The name in that languageaté 50 caracteres |
Exemplo
curl -X PATCH "https://delimenu.co/api/v1/restaurants/pizzeria-roma/categories/k3Qm8vXb2LpN7wRt1YaZ" \
-H "Authorization: Bearer dmk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{"name":"Pizzas artesanales"}'
Resposta 200
{
"category": {
"id": "k3Qm8vXb2LpN7wRt1YaZ",
"name": "Pizzas artesanales"
}
}
Campos da resposta
| Campo | Tipo | Descrição |
|---|
category* | object | |
id* | string | |
name* | string | |
translations | object | The name in other languages, keyed by language code, e.g. { "en": { "name": "Large" } }. Absent when nothing is translated; a language missing here falls back to `name`. |
es | object | |
name | string | |
en | object | |
name | string | |
pt | object | |
name | string | |
fr | object | |
name | string | |
de | object | |
name | string | |
it | object | |
name | string | |
Erros
400 VALIDATION — a field is missing or malformed; `issues` names it401 UNAUTHORIZED — the key is missing, unknown or revoked402 NOT_PREMIUM — the restaurant has no active subscription or trial403 FORBIDDEN — the key lacks the scope this endpoint needs404 NOT_FOUND — no such restaurant in this account, or no such item in it500 INTERNAL — something failed on our side; retry in a moment
delete/restaurants/{ref}/categories/{id}requer menu:writedeleteCategory
Delete a category
Refused with 409 while the category still has products, unless delete_products is true, in which case the products are deleted too.
Parâmetros
| Nome | Onde | Descrição |
|---|
ref* | caminho | The restaurant identifier (the slug in its public URL, e.g. "pizzeria-roma") or its id, as returned by GET /restaurants |
id* | caminho | The category id, from GET /restaurants/{ref}/categories |
delete_products | query | Delete the products in the category too. Without it a non-empty category is refused with 409. |
Exemplo
curl -X DELETE "https://delimenu.co/api/v1/restaurants/pizzeria-roma/categories/k3Qm8vXb2LpN7wRt1YaZ?delete_products=true" \
-H "Authorization: Bearer dmk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
Resposta 200
{
"deleted": "k3Qm8vXb2LpN7wRt1YaZ",
"deleted_products": 3
}
Campos da resposta
| Campo | Tipo | Descrição |
|---|
deleted* | string | |
deleted_products* | integer | min -9007199254740991 · max 9007199254740991 |
Erros
400 VALIDATION — a field is missing or malformed; `issues` names it401 UNAUTHORIZED — the key is missing, unknown or revoked402 NOT_PREMIUM — the restaurant has no active subscription or trial403 FORBIDDEN — the key lacks the scope this endpoint needs404 NOT_FOUND — no such restaurant in this account, or no such item in it409 CONFLICT — the category still has products500 INTERNAL — something failed on our side; retry in a moment
put/restaurants/{ref}/categories/{id}/product-orderrequer menu:writereorderProducts
Reorder the products of a category
Pass every product id of that category exactly once, in the new order.
Parâmetros
| Nome | Onde | Descrição |
|---|
ref* | caminho | The restaurant identifier (the slug in its public URL, e.g. "pizzeria-roma") or its id, as returned by GET /restaurants |
id* | caminho | The category id, from GET /restaurants/{ref}/categories |
Corpo (JSON)
| Campo | Tipo | Descrição |
|---|
product_ids* | string[] | Every product id of the category exactly once, in the new display order |
Exemplo
curl -X PUT "https://delimenu.co/api/v1/restaurants/pizzeria-roma/categories/k3Qm8vXb2LpN7wRt1YaZ/product-order" \
-H "Authorization: Bearer dmk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{"product_ids":["q2Jk5Lm8Np1Qr4St7Uv0","p9Hs4TqL2mNc8VbX6Rdy"]}'
Resposta 200
{
"category_id": "k3Qm8vXb2LpN7wRt1YaZ",
"order_products": [
"q2Jk5Lm8Np1Qr4St7Uv0",
"p9Hs4TqL2mNc8VbX6Rdy"
]
}
Campos da resposta
| Campo | Tipo | Descrição |
|---|
category_id* | string | |
order_products* | string[] | |
Erros
400 VALIDATION — a field is missing or malformed; `issues` names it401 UNAUTHORIZED — the key is missing, unknown or revoked402 NOT_PREMIUM — the restaurant has no active subscription or trial403 FORBIDDEN — the key lacks the scope this endpoint needs404 NOT_FOUND — no such restaurant in this account, or no such item in it500 INTERNAL — something failed on our side; retry in a moment
Products
get/restaurants/{ref}/productsrequer menu:readlistProducts
List or search the products
Every product, hidden ones included and flagged. With q, only those whose name or description contains the text.
Parâmetros
| Nome | Onde | Descrição |
|---|
ref* | caminho | The restaurant identifier (the slug in its public URL, e.g. "pizzeria-roma") or its id, as returned by GET /restaurants |
q | query | Only products whose name or description contains this text (accent- and case-insensitive)até 100 caracteres |
Exemplo
curl -X GET "https://delimenu.co/api/v1/restaurants/pizzeria-roma/products?q=margarita" \
-H "Authorization: Bearer dmk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
Resposta 200
{
"products": [
{
"id": "p9Hs4TqL2mNc8VbX6Rdy",
"category_id": "k3Qm8vXb2LpN7wRt1YaZ",
"name": "Pizza Margarita",
"price": 32000,
"original_price": 38000,
"description": "Tomate, mozzarella y albahaca fresca",
"available": true,
"hidden": false,
"has_image": true,
"image_url": "https://firebasestorage.googleapis.com/v0/b/example/o/margarita_1080x1080.jpg?alt=media",
"variants": [
{
"id": "26fed9f6-2e3b-4310-b899-5641bf98e2f0",
"name": "Tamaño",
"min_selections": 1,
"max_selections": 1,
"options": [
{
"id": "5c4f8896-06a3-4482-99d1-185a909d0415",
"name": "Personal",
"price": 0,
"show": true
},
{
"id": "b1e0c4d2-7f3a-4c8e-9d21-0a5f6e7b8c9d",
"name": "Familiar",
"price": 12000,
"show": true
}
]
}
]
}
],
"count": 1
}
Campos da resposta
| Campo | Tipo | Descrição |
|---|
products* | object[] | |
id* | string | |
category_id* | string | |
name* | string | |
price* | number | Major units in the restaurant's currency |
original_price | number | Present while the product is on promotion |
description | string | |
available* | boolean | false renders "Sin stock" |
hidden* | boolean | true keeps it off the public menu |
has_image* | boolean | |
image_url | string | |
variants* | object[] | |
id* | string | |
name* | string | |
min_selections* | integer | min -9007199254740991 · max 9007199254740991 |
max_selections* | integer | 0 is no limitmin -9007199254740991 · max 9007199254740991 |
max_per_option | integer | min -9007199254740991 · max 9007199254740991 |
options* | object[] | |
id* | string | |
name* | string | |
price* | number | Extra charge on top of the product price |
show* | boolean | |
translations | object | The name in other languages, keyed by language code, e.g. { "en": { "name": "Large" } }. Absent when nothing is translated; a language missing here falls back to `name`. |
translations | object | The name in other languages, keyed by language code, e.g. { "en": { "name": "Large" } }. Absent when nothing is translated; a language missing here falls back to `name`. |
es | object | |
en | object | |
pt | object | |
fr | object | |
de | object | |
it | object | |
translations | object | Name and description in other languages, keyed by language code. Absent when nothing is translated; a language or field missing here falls back to `name` / `description`. |
es | object | |
name | string | |
description | string | |
en | object | |
name | string | |
description | string | |
pt | object | |
name | string | |
description | string | |
fr | object | |
name | string | |
description | string | |
de | object | |
name | string | |
description | string | |
it | object | |
name | string | |
description | string | |
count* | integer | min -9007199254740991 · max 9007199254740991 |
Erros
400 VALIDATION — a field is missing or malformed; `issues` names it401 UNAUTHORIZED — the key is missing, unknown or revoked402 NOT_PREMIUM — the restaurant has no active subscription or trial403 FORBIDDEN — the key lacks the scope this endpoint needs404 NOT_FOUND — no such restaurant in this account, or no such item in it500 INTERNAL — something failed on our side; retry in a moment
post/restaurants/{ref}/productsrequer menu:writecreateProduct
Create a product
Adds a product to a category, at the end. Price is in the restaurant's currency, major units. Variants are optional choices (sizes, extras) with their own option prices.
Parâmetros
| Nome | Onde | Descrição |
|---|
ref* | caminho | The restaurant identifier (the slug in its public URL, e.g. "pizzeria-roma") or its id, as returned by GET /restaurants |
Corpo (JSON)
| Campo | Tipo | Descrição |
|---|
category_id* | string | The category the product belongs to (from get_menu)até 128 caracteres |
name* | string | Product nameaté 50 caracteres |
price* | number | Price in the restaurant's currency, in major units (12500 for COP $12.000, 5.99 for USD $5.99). Never cents.min 0 |
description | string | Optional description. Pass an empty string to remove it.até 3000 caracteres |
original_price | number | null | The price before a promotion. Must be greater than price; the menu shows it struck through. Pass null to end the promotion.min 0 |
available | boolean | false shows the product as "Sin stock": visible but not orderable. Default true. |
hidden | boolean | true keeps the product off the public menu entirely. Default false. |
variants | object[] | Choices the diner makes (sizes, extras). Replaces the whole list when provided; a variant or option sent without translations keeps the ones stored under the same name. |
name* | string | Variant name, e.g. "Tamaño" or "Adiciones"até 50 caracteres |
translations | object | The variant name in the menu's other languages, e.g. { "en": { "name": "Size" } }. Omit it to keep the translations of the stored variant with the same name; {} removes them. |
es | object | |
name | string | The name in that languageaté 50 caracteres |
en | object | |
name | string | The name in that languageaté 50 caracteres |
pt | object | |
name | string | The name in that languageaté 50 caracteres |
fr | object | |
name | string | The name in that languageaté 50 caracteres |
de | object | |
name | string | The name in that languageaté 50 caracteres |
it | object | |
name | string | The name in that languageaté 50 caracteres |
min_selections | integer | Options the diner must pick. 0 makes the variant optional.min 0 · max 9007199254740991 · padrão 0 |
max_selections | integer | Options the diner may pick. 0 is no limit; 1 renders a single choice.min 0 · max 9007199254740991 · padrão 0 |
max_per_option | integer | How many times one option may be repeated. Omit or 0 for no limit.min 0 · max 9007199254740991 |
options* | object[] | |
name* | string | Option name, e.g. "Grande"até 50 caracteres |
translations | object | The option name in the menu's other languages, e.g. { "en": { "name": "Large" } }. Omit it to keep the translations of the stored option with the same name; {} removes them. |
es | object | |
en | object | |
pt | object | |
fr | object | |
de | object | |
it | object | |
price | number | Extra charge for this option on top of the product price. 0 when it costs nothing.min 0 · padrão 0 |
show | boolean | false hides the option from diners without deleting itpadrão true |
translations | object | The product name and description in the menu's other languages, e.g. { "en": { "name": "Lemonade", "description": "Freshly squeezed" } }. Keyed by language code (es, en, pt, fr, de, it). Shown to diners who read the menu in that language; a language without a translation falls back to the original text, and an entry in the menu's primary language is ignored. Replaces the whole map when provided; {} removes every translation. Variant and option names are translated inside each variant and option. |
es | object | |
name | string | The product name in that languageaté 50 caracteres |
description | string | The description in that languageaté 3000 caracteres |
en | object | |
name | string | The product name in that languageaté 50 caracteres |
description | string | The description in that languageaté 3000 caracteres |
pt | object | |
name | string | The product name in that languageaté 50 caracteres |
description | string | The description in that languageaté 3000 caracteres |
fr | object | |
name | string | The product name in that languageaté 50 caracteres |
description | string | The description in that languageaté 3000 caracteres |
de | object | |
name | string | The product name in that languageaté 50 caracteres |
description | string | The description in that languageaté 3000 caracteres |
it | object | |
name | string | The product name in that languageaté 50 caracteres |
description | string | The description in that languageaté 3000 caracteres |
Exemplo
curl -X POST "https://delimenu.co/api/v1/restaurants/pizzeria-roma/products" \
-H "Authorization: Bearer dmk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{"category_id":"k3Qm8vXb2LpN7wRt1YaZ","name":"Pizza Margarita","price":32000,"description":"Tomate, mozzarella y albahaca fresca","variants":[{"name":"Tamaño","min_selections":1,"max_selections":1,"options":[{"name":"Personal","price":0},{"name":"Familiar","price":12000}]}]}'
Resposta 201
{
"product": {
"id": "p9Hs4TqL2mNc8VbX6Rdy",
"category_id": "k3Qm8vXb2LpN7wRt1YaZ",
"name": "Pizza Margarita",
"price": 32000,
"description": "Tomate, mozzarella y albahaca fresca",
"available": true,
"hidden": false,
"has_image": true,
"image_url": "https://firebasestorage.googleapis.com/v0/b/example/o/margarita_1080x1080.jpg?alt=media",
"variants": [
{
"id": "26fed9f6-2e3b-4310-b899-5641bf98e2f0",
"name": "Tamaño",
"min_selections": 1,
"max_selections": 1,
"options": [
{
"id": "5c4f8896-06a3-4482-99d1-185a909d0415",
"name": "Personal",
"price": 0,
"show": true
},
{
"id": "b1e0c4d2-7f3a-4c8e-9d21-0a5f6e7b8c9d",
"name": "Familiar",
"price": 12000,
"show": true
}
]
}
]
}
}
Campos da resposta
| Campo | Tipo | Descrição |
|---|
product* | object | |
id* | string | |
category_id* | string | |
name* | string | |
price* | number | Major units in the restaurant's currency |
original_price | number | Present while the product is on promotion |
description | string | |
available* | boolean | false renders "Sin stock" |
hidden* | boolean | true keeps it off the public menu |
has_image* | boolean | |
image_url | string | |
variants* | object[] | |
id* | string | |
name* | string | |
min_selections* | integer | min -9007199254740991 · max 9007199254740991 |
max_selections* | integer | 0 is no limitmin -9007199254740991 · max 9007199254740991 |
max_per_option | integer | min -9007199254740991 · max 9007199254740991 |
options* | object[] | |
id* | string | |
name* | string | |
price* | number | Extra charge on top of the product price |
show* | boolean | |
translations | object | The name in other languages, keyed by language code, e.g. { "en": { "name": "Large" } }. Absent when nothing is translated; a language missing here falls back to `name`. |
translations | object | The name in other languages, keyed by language code, e.g. { "en": { "name": "Large" } }. Absent when nothing is translated; a language missing here falls back to `name`. |
es | object | |
en | object | |
pt | object | |
fr | object | |
de | object | |
it | object | |
translations | object | Name and description in other languages, keyed by language code. Absent when nothing is translated; a language or field missing here falls back to `name` / `description`. |
es | object | |
name | string | |
description | string | |
en | object | |
name | string | |
description | string | |
pt | object | |
name | string | |
description | string | |
fr | object | |
name | string | |
description | string | |
de | object | |
name | string | |
description | string | |
it | object | |
name | string | |
description | string | |
Erros
400 VALIDATION — a field is missing or malformed; `issues` names it401 UNAUTHORIZED — the key is missing, unknown or revoked402 NOT_PREMIUM — the restaurant has no active subscription or trial403 FORBIDDEN — the key lacks the scope this endpoint needs404 NOT_FOUND — no such restaurant in this account, or no such item in it500 INTERNAL — something failed on our side; retry in a moment
patch/restaurants/{ref}/productsrequer menu:writebulkUpdateProducts
Update many products
Applies a patch to up to 50 products in one write — for "raise every price 10%" or "mark these as out of stock". Same fields as updating one product.
Parâmetros
| Nome | Onde | Descrição |
|---|
ref* | caminho | The restaurant identifier (the slug in its public URL, e.g. "pizzeria-roma") or its id, as returned by GET /restaurants |
Corpo (JSON)
| Campo | Tipo | Descrição |
|---|
updates* | object[] | Up to 50 products, each with only the fields to change |
product_id* | string | até 128 caracteres |
changes* | object | |
category_id | string | Move the product to another categoryaté 128 caracteres |
name | string | Product nameaté 50 caracteres |
price | number | Price in the restaurant's currency, in major units (12500 for COP $12.000, 5.99 for USD $5.99). Never cents.min 0 |
description | string | Optional description. Pass an empty string to remove it.até 3000 caracteres |
original_price | number | null | The price before a promotion. Must be greater than price; the menu shows it struck through. Pass null to end the promotion.min 0 |
available | boolean | false shows the product as "Sin stock": visible but not orderable. Default true. |
hidden | boolean | true keeps the product off the public menu entirely. Default false. |
variants | object[] | Choices the diner makes (sizes, extras). Replaces the whole list when provided; a variant or option sent without translations keeps the ones stored under the same name. |
name* | string | Variant name, e.g. "Tamaño" or "Adiciones"até 50 caracteres |
translations | object | The variant name in the menu's other languages, e.g. { "en": { "name": "Size" } }. Omit it to keep the translations of the stored variant with the same name; {} removes them. |
min_selections | integer | Options the diner must pick. 0 makes the variant optional.min 0 · max 9007199254740991 · padrão 0 |
max_selections | integer | Options the diner may pick. 0 is no limit; 1 renders a single choice.min 0 · max 9007199254740991 · padrão 0 |
max_per_option | integer | How many times one option may be repeated. Omit or 0 for no limit.min 0 · max 9007199254740991 |
options* | object[] | |
translations | object | The product name and description in the menu's other languages, e.g. { "en": { "name": "Lemonade", "description": "Freshly squeezed" } }. Keyed by language code (es, en, pt, fr, de, it). Shown to diners who read the menu in that language; a language without a translation falls back to the original text, and an entry in the menu's primary language is ignored. Replaces the whole map when provided; {} removes every translation. Variant and option names are translated inside each variant and option. |
es | object | |
en | object | |
pt | object | |
fr | object | |
de | object | |
it | object | |
Exemplo
curl -X PATCH "https://delimenu.co/api/v1/restaurants/pizzeria-roma/products" \
-H "Authorization: Bearer dmk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{"updates":[{"product_id":"p9Hs4TqL2mNc8VbX6Rdy","changes":{"price":35000}},{"product_id":"q2Jk5Lm8Np1Qr4St7Uv0","changes":{"available":false}}]}'
Resposta 200
{
"updated": 2
}
Campos da resposta
| Campo | Tipo | Descrição |
|---|
updated* | integer | min -9007199254740991 · max 9007199254740991 |
Erros
400 VALIDATION — a field is missing or malformed; `issues` names it401 UNAUTHORIZED — the key is missing, unknown or revoked402 NOT_PREMIUM — the restaurant has no active subscription or trial403 FORBIDDEN — the key lacks the scope this endpoint needs404 NOT_FOUND — no such restaurant in this account, or no such item in it500 INTERNAL — something failed on our side; retry in a moment
get/restaurants/{ref}/products/{id}requer menu:readgetProduct
Get a product
translations is present only where something is translated: on the product (name, description) and inside each variant and option (name).
Parâmetros
| Nome | Onde | Descrição |
|---|
ref* | caminho | The restaurant identifier (the slug in its public URL, e.g. "pizzeria-roma") or its id, as returned by GET /restaurants |
id* | caminho | The product id, from GET /restaurants/{ref}/products |
Exemplo
curl -X GET "https://delimenu.co/api/v1/restaurants/pizzeria-roma/products/p9Hs4TqL2mNc8VbX6Rdy" \
-H "Authorization: Bearer dmk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
Resposta 200
{
"product": {
"id": "p9Hs4TqL2mNc8VbX6Rdy",
"category_id": "k3Qm8vXb2LpN7wRt1YaZ",
"name": "Pizza Margarita",
"price": 32000,
"original_price": 38000,
"description": "Tomate, mozzarella y albahaca fresca",
"available": true,
"hidden": false,
"has_image": true,
"image_url": "https://firebasestorage.googleapis.com/v0/b/example/o/margarita_1080x1080.jpg?alt=media",
"variants": [
{
"id": "26fed9f6-2e3b-4310-b899-5641bf98e2f0",
"name": "Tamaño",
"min_selections": 1,
"max_selections": 1,
"options": [
{
"id": "5c4f8896-06a3-4482-99d1-185a909d0415",
"name": "Personal",
"price": 0,
"show": true
},
{
"id": "b1e0c4d2-7f3a-4c8e-9d21-0a5f6e7b8c9d",
"name": "Familiar",
"price": 12000,
"show": true,
"translations": {
"en": {
"name": "Family"
}
}
}
],
"translations": {
"en": {
"name": "Size"
}
}
}
],
"translations": {
"en": {
"name": "Margherita Pizza",
"description": "Tomato, mozzarella and fresh basil"
}
}
}
}
Campos da resposta
| Campo | Tipo | Descrição |
|---|
product* | object | |
id* | string | |
category_id* | string | |
name* | string | |
price* | number | Major units in the restaurant's currency |
original_price | number | Present while the product is on promotion |
description | string | |
available* | boolean | false renders "Sin stock" |
hidden* | boolean | true keeps it off the public menu |
has_image* | boolean | |
image_url | string | |
variants* | object[] | |
id* | string | |
name* | string | |
min_selections* | integer | min -9007199254740991 · max 9007199254740991 |
max_selections* | integer | 0 is no limitmin -9007199254740991 · max 9007199254740991 |
max_per_option | integer | min -9007199254740991 · max 9007199254740991 |
options* | object[] | |
id* | string | |
name* | string | |
price* | number | Extra charge on top of the product price |
show* | boolean | |
translations | object | The name in other languages, keyed by language code, e.g. { "en": { "name": "Large" } }. Absent when nothing is translated; a language missing here falls back to `name`. |
translations | object | The name in other languages, keyed by language code, e.g. { "en": { "name": "Large" } }. Absent when nothing is translated; a language missing here falls back to `name`. |
es | object | |
en | object | |
pt | object | |
fr | object | |
de | object | |
it | object | |
translations | object | Name and description in other languages, keyed by language code. Absent when nothing is translated; a language or field missing here falls back to `name` / `description`. |
es | object | |
name | string | |
description | string | |
en | object | |
name | string | |
description | string | |
pt | object | |
name | string | |
description | string | |
fr | object | |
name | string | |
description | string | |
de | object | |
name | string | |
description | string | |
it | object | |
name | string | |
description | string | |
Erros
401 UNAUTHORIZED — the key is missing, unknown or revoked402 NOT_PREMIUM — the restaurant has no active subscription or trial403 FORBIDDEN — the key lacks the scope this endpoint needs404 NOT_FOUND — no such restaurant in this account, or no such item in it500 INTERNAL — something failed on our side; retry in a moment
patch/restaurants/{ref}/products/{id}requer menu:writeupdateProduct
Update a product
Changes only the fields given: name, price, description, original_price (promotion), available, hidden, variants (replaces all), translations (replaces all) or category_id (moves it). Raising price above an existing original_price ends the promotion. A variant or option sent without translations keeps the ones stored under the same name, so repricing an option does not erase them; send translations: {} on it to remove them.
Parâmetros
| Nome | Onde | Descrição |
|---|
ref* | caminho | The restaurant identifier (the slug in its public URL, e.g. "pizzeria-roma") or its id, as returned by GET /restaurants |
id* | caminho | The product id, from GET /restaurants/{ref}/products |
Corpo (JSON)
| Campo | Tipo | Descrição |
|---|
category_id | string | Move the product to another categoryaté 128 caracteres |
name | string | Product nameaté 50 caracteres |
price | number | Price in the restaurant's currency, in major units (12500 for COP $12.000, 5.99 for USD $5.99). Never cents.min 0 |
description | string | Optional description. Pass an empty string to remove it.até 3000 caracteres |
original_price | number | null | The price before a promotion. Must be greater than price; the menu shows it struck through. Pass null to end the promotion.min 0 |
available | boolean | false shows the product as "Sin stock": visible but not orderable. Default true. |
hidden | boolean | true keeps the product off the public menu entirely. Default false. |
variants | object[] | Choices the diner makes (sizes, extras). Replaces the whole list when provided; a variant or option sent without translations keeps the ones stored under the same name. |
name* | string | Variant name, e.g. "Tamaño" or "Adiciones"até 50 caracteres |
translations | object | The variant name in the menu's other languages, e.g. { "en": { "name": "Size" } }. Omit it to keep the translations of the stored variant with the same name; {} removes them. |
es | object | |
name | string | The name in that languageaté 50 caracteres |
en | object | |
name | string | The name in that languageaté 50 caracteres |
pt | object | |
name | string | The name in that languageaté 50 caracteres |
fr | object | |
name | string | The name in that languageaté 50 caracteres |
de | object | |
name | string | The name in that languageaté 50 caracteres |
it | object | |
name | string | The name in that languageaté 50 caracteres |
min_selections | integer | Options the diner must pick. 0 makes the variant optional.min 0 · max 9007199254740991 · padrão 0 |
max_selections | integer | Options the diner may pick. 0 is no limit; 1 renders a single choice.min 0 · max 9007199254740991 · padrão 0 |
max_per_option | integer | How many times one option may be repeated. Omit or 0 for no limit.min 0 · max 9007199254740991 |
options* | object[] | |
name* | string | Option name, e.g. "Grande"até 50 caracteres |
translations | object | The option name in the menu's other languages, e.g. { "en": { "name": "Large" } }. Omit it to keep the translations of the stored option with the same name; {} removes them. |
es | object | |
en | object | |
pt | object | |
fr | object | |
de | object | |
it | object | |
price | number | Extra charge for this option on top of the product price. 0 when it costs nothing.min 0 · padrão 0 |
show | boolean | false hides the option from diners without deleting itpadrão true |
translations | object | The product name and description in the menu's other languages, e.g. { "en": { "name": "Lemonade", "description": "Freshly squeezed" } }. Keyed by language code (es, en, pt, fr, de, it). Shown to diners who read the menu in that language; a language without a translation falls back to the original text, and an entry in the menu's primary language is ignored. Replaces the whole map when provided; {} removes every translation. Variant and option names are translated inside each variant and option. |
es | object | |
name | string | The product name in that languageaté 50 caracteres |
description | string | The description in that languageaté 3000 caracteres |
en | object | |
name | string | The product name in that languageaté 50 caracteres |
description | string | The description in that languageaté 3000 caracteres |
pt | object | |
name | string | The product name in that languageaté 50 caracteres |
description | string | The description in that languageaté 3000 caracteres |
fr | object | |
name | string | The product name in that languageaté 50 caracteres |
description | string | The description in that languageaté 3000 caracteres |
de | object | |
name | string | The product name in that languageaté 50 caracteres |
description | string | The description in that languageaté 3000 caracteres |
it | object | |
name | string | The product name in that languageaté 50 caracteres |
description | string | The description in that languageaté 3000 caracteres |
Exemplo
curl -X PATCH "https://delimenu.co/api/v1/restaurants/pizzeria-roma/products/p9Hs4TqL2mNc8VbX6Rdy" \
-H "Authorization: Bearer dmk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{"price":30000,"original_price":38000,"translations":{"en":{"name":"Margherita Pizza","description":"Tomato, mozzarella and fresh basil"}}}'
Resposta 200
{
"product": {
"id": "p9Hs4TqL2mNc8VbX6Rdy",
"category_id": "k3Qm8vXb2LpN7wRt1YaZ",
"name": "Pizza Margarita",
"price": 30000,
"original_price": 38000,
"description": "Tomate, mozzarella y albahaca fresca",
"available": true,
"hidden": false,
"has_image": true,
"image_url": "https://firebasestorage.googleapis.com/v0/b/example/o/margarita_1080x1080.jpg?alt=media",
"variants": [
{
"id": "26fed9f6-2e3b-4310-b899-5641bf98e2f0",
"name": "Tamaño",
"min_selections": 1,
"max_selections": 1,
"options": [
{
"id": "5c4f8896-06a3-4482-99d1-185a909d0415",
"name": "Personal",
"price": 0,
"show": true
},
{
"id": "b1e0c4d2-7f3a-4c8e-9d21-0a5f6e7b8c9d",
"name": "Familiar",
"price": 12000,
"show": true,
"translations": {
"en": {
"name": "Family"
}
}
}
],
"translations": {
"en": {
"name": "Size"
}
}
}
],
"translations": {
"en": {
"name": "Margherita Pizza",
"description": "Tomato, mozzarella and fresh basil"
}
}
}
}
Campos da resposta
| Campo | Tipo | Descrição |
|---|
product* | object | |
id* | string | |
category_id* | string | |
name* | string | |
price* | number | Major units in the restaurant's currency |
original_price | number | Present while the product is on promotion |
description | string | |
available* | boolean | false renders "Sin stock" |
hidden* | boolean | true keeps it off the public menu |
has_image* | boolean | |
image_url | string | |
variants* | object[] | |
id* | string | |
name* | string | |
min_selections* | integer | min -9007199254740991 · max 9007199254740991 |
max_selections* | integer | 0 is no limitmin -9007199254740991 · max 9007199254740991 |
max_per_option | integer | min -9007199254740991 · max 9007199254740991 |
options* | object[] | |
id* | string | |
name* | string | |
price* | number | Extra charge on top of the product price |
show* | boolean | |
translations | object | The name in other languages, keyed by language code, e.g. { "en": { "name": "Large" } }. Absent when nothing is translated; a language missing here falls back to `name`. |
translations | object | The name in other languages, keyed by language code, e.g. { "en": { "name": "Large" } }. Absent when nothing is translated; a language missing here falls back to `name`. |
es | object | |
en | object | |
pt | object | |
fr | object | |
de | object | |
it | object | |
translations | object | Name and description in other languages, keyed by language code. Absent when nothing is translated; a language or field missing here falls back to `name` / `description`. |
es | object | |
name | string | |
description | string | |
en | object | |
name | string | |
description | string | |
pt | object | |
name | string | |
description | string | |
fr | object | |
name | string | |
description | string | |
de | object | |
name | string | |
description | string | |
it | object | |
name | string | |
description | string | |
Erros
400 VALIDATION — a field is missing or malformed; `issues` names it401 UNAUTHORIZED — the key is missing, unknown or revoked402 NOT_PREMIUM — the restaurant has no active subscription or trial403 FORBIDDEN — the key lacks the scope this endpoint needs404 NOT_FOUND — no such restaurant in this account, or no such item in it500 INTERNAL — something failed on our side; retry in a moment
delete/restaurants/{ref}/products/{id}requer menu:writedeleteProduct
Delete a product
Permanent, photo included. Prefer hidden: true when the owner may want it back.
Parâmetros
| Nome | Onde | Descrição |
|---|
ref* | caminho | The restaurant identifier (the slug in its public URL, e.g. "pizzeria-roma") or its id, as returned by GET /restaurants |
id* | caminho | The product id, from GET /restaurants/{ref}/products |
Exemplo
curl -X DELETE "https://delimenu.co/api/v1/restaurants/pizzeria-roma/products/p9Hs4TqL2mNc8VbX6Rdy" \
-H "Authorization: Bearer dmk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
Resposta 200
{
"deleted": "p9Hs4TqL2mNc8VbX6Rdy"
}
Campos da resposta
| Campo | Tipo | Descrição |
|---|
deleted* | string | |
Erros
401 UNAUTHORIZED — the key is missing, unknown or revoked402 NOT_PREMIUM — the restaurant has no active subscription or trial403 FORBIDDEN — the key lacks the scope this endpoint needs404 NOT_FOUND — no such restaurant in this account, or no such item in it500 INTERNAL — something failed on our side; retry in a moment
Images
Photos, logo and banner from a public https URL (JPG, PNG or WEBP, up to 5 MB).
put/restaurants/{ref}/images/{kind}requer menu:writesetRestaurantImage
Set the logo or the banner
Downloads the image at a public https URL and sets it as the logo or the banner, replacing the current one. Takes a few seconds while the image is optimised.
Parâmetros
| Nome | Onde | Descrição |
|---|
ref* | caminho | The restaurant identifier (the slug in its public URL, e.g. "pizzeria-roma") or its id, as returned by GET /restaurants |
kind* | caminho | "logo" is shown at 512×512; "banner" is the header image, 1080×1080 max"logo" | "banner" |
Corpo (JSON)
| Campo | Tipo | Descrição |
|---|
image_url* | string | Public https URL of a JPG, PNG or WEBP image up to 5 MBaté 2048 caracteres |
Exemplo
curl -X PUT "https://delimenu.co/api/v1/restaurants/pizzeria-roma/images/logo" \
-H "Authorization: Bearer dmk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{"image_url":"https://example.com/logo.png"}'
Resposta 200
{
"restaurant": {
"id": "aR3kX9pLm2QzT7vN4bYc",
"identifier": "pizzeria-roma",
"name": "Pizzería Roma",
"currency": "COP",
"type": "whatsapp",
"phone": "+573001234567",
"address": "Calle 10 # 5-20, Bogotá",
"menu_url": "https://delimenu.co/pizzeria-roma",
"logo_url": "https://firebasestorage.googleapis.com/v0/b/example/o/logo_512x512.png?alt=media",
"banner_url": null,
"language": "es",
"languages": [
"en"
],
"trial_active": false
}
}
Campos da resposta
| Campo | Tipo | Descrição |
|---|
restaurant* | object | |
id* | string | |
identifier* | string | |
name* | string | |
currency* | string | ISO 4217 code every price is in |
type* | "whatsapp" | "read_only" | "whatsapp" takes orders by WhatsApp; "read_only" only shows the menu |
phone* | string | null | WhatsApp number in E.164, when ordering is enabled |
address* | string | null | |
menu_url* | string | |
logo_url* | string | null | |
banner_url* | string | null | |
language* | "es" | "en" | "pt" | "fr" | "de" | "it" | The primary language: the one name, description and every untranslated field are written in |
languages* | "es" | "en" | "pt" | "fr" | "de" | "it"[] | The additional languages diners can read the menu in, translated through `translations`. Empty for a menu in one language. |
trial_active* | boolean | |
Erros
400 VALIDATION — a field is missing or malformed; `issues` names it401 UNAUTHORIZED — the key is missing, unknown or revoked402 NOT_PREMIUM — the restaurant has no active subscription or trial403 FORBIDDEN — the key lacks the scope this endpoint needs404 NOT_FOUND — no such restaurant in this account, or no such item in it500 INTERNAL — something failed on our side; retry in a moment
delete/restaurants/{ref}/images/{kind}requer menu:writeremoveRestaurantImage
Remove the logo or the banner
Parâmetros
| Nome | Onde | Descrição |
|---|
ref* | caminho | The restaurant identifier (the slug in its public URL, e.g. "pizzeria-roma") or its id, as returned by GET /restaurants |
kind* | caminho | "logo" is shown at 512×512; "banner" is the header image, 1080×1080 max"logo" | "banner" |
Exemplo
curl -X DELETE "https://delimenu.co/api/v1/restaurants/pizzeria-roma/images/logo" \
-H "Authorization: Bearer dmk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
Resposta 200
{
"restaurant": {
"id": "aR3kX9pLm2QzT7vN4bYc",
"identifier": "pizzeria-roma",
"name": "Pizzería Roma",
"currency": "COP",
"type": "whatsapp",
"phone": "+573001234567",
"address": "Calle 10 # 5-20, Bogotá",
"menu_url": "https://delimenu.co/pizzeria-roma",
"logo_url": null,
"banner_url": null,
"language": "es",
"languages": [
"en"
],
"trial_active": false
}
}
Campos da resposta
| Campo | Tipo | Descrição |
|---|
restaurant* | object | |
id* | string | |
identifier* | string | |
name* | string | |
currency* | string | ISO 4217 code every price is in |
type* | "whatsapp" | "read_only" | "whatsapp" takes orders by WhatsApp; "read_only" only shows the menu |
phone* | string | null | WhatsApp number in E.164, when ordering is enabled |
address* | string | null | |
menu_url* | string | |
logo_url* | string | null | |
banner_url* | string | null | |
language* | "es" | "en" | "pt" | "fr" | "de" | "it" | The primary language: the one name, description and every untranslated field are written in |
languages* | "es" | "en" | "pt" | "fr" | "de" | "it"[] | The additional languages diners can read the menu in, translated through `translations`. Empty for a menu in one language. |
trial_active* | boolean | |
Erros
401 UNAUTHORIZED — the key is missing, unknown or revoked402 NOT_PREMIUM — the restaurant has no active subscription or trial403 FORBIDDEN — the key lacks the scope this endpoint needs404 NOT_FOUND — no such restaurant in this account, or no such item in it500 INTERNAL — something failed on our side; retry in a moment
put/restaurants/{ref}/products/{id}/imagerequer menu:writesetProductImage
Set the product photo
Downloads the image at a public https URL and makes it the product photo, replacing the current one. Takes a few seconds while the image is optimised.
Parâmetros
| Nome | Onde | Descrição |
|---|
ref* | caminho | The restaurant identifier (the slug in its public URL, e.g. "pizzeria-roma") or its id, as returned by GET /restaurants |
id* | caminho | The product id, from GET /restaurants/{ref}/products |
Corpo (JSON)
| Campo | Tipo | Descrição |
|---|
image_url* | string | Public https URL of a JPG, PNG or WEBP image up to 5 MBaté 2048 caracteres |
Exemplo
curl -X PUT "https://delimenu.co/api/v1/restaurants/pizzeria-roma/products/p9Hs4TqL2mNc8VbX6Rdy/image" \
-H "Authorization: Bearer dmk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{"image_url":"https://example.com/fotos/margarita.jpg"}'
Resposta 200
{
"product": {
"id": "p9Hs4TqL2mNc8VbX6Rdy",
"category_id": "k3Qm8vXb2LpN7wRt1YaZ",
"name": "Pizza Margarita",
"price": 32000,
"original_price": 38000,
"description": "Tomate, mozzarella y albahaca fresca",
"available": true,
"hidden": false,
"has_image": true,
"image_url": "https://firebasestorage.googleapis.com/v0/b/example/o/margarita_1080x1080.jpg?alt=media",
"variants": [
{
"id": "26fed9f6-2e3b-4310-b899-5641bf98e2f0",
"name": "Tamaño",
"min_selections": 1,
"max_selections": 1,
"options": [
{
"id": "5c4f8896-06a3-4482-99d1-185a909d0415",
"name": "Personal",
"price": 0,
"show": true
},
{
"id": "b1e0c4d2-7f3a-4c8e-9d21-0a5f6e7b8c9d",
"name": "Familiar",
"price": 12000,
"show": true
}
]
}
]
}
}
Campos da resposta
| Campo | Tipo | Descrição |
|---|
product* | object | |
id* | string | |
category_id* | string | |
name* | string | |
price* | number | Major units in the restaurant's currency |
original_price | number | Present while the product is on promotion |
description | string | |
available* | boolean | false renders "Sin stock" |
hidden* | boolean | true keeps it off the public menu |
has_image* | boolean | |
image_url | string | |
variants* | object[] | |
id* | string | |
name* | string | |
min_selections* | integer | min -9007199254740991 · max 9007199254740991 |
max_selections* | integer | 0 is no limitmin -9007199254740991 · max 9007199254740991 |
max_per_option | integer | min -9007199254740991 · max 9007199254740991 |
options* | object[] | |
id* | string | |
name* | string | |
price* | number | Extra charge on top of the product price |
show* | boolean | |
translations | object | The name in other languages, keyed by language code, e.g. { "en": { "name": "Large" } }. Absent when nothing is translated; a language missing here falls back to `name`. |
translations | object | The name in other languages, keyed by language code, e.g. { "en": { "name": "Large" } }. Absent when nothing is translated; a language missing here falls back to `name`. |
es | object | |
en | object | |
pt | object | |
fr | object | |
de | object | |
it | object | |
translations | object | Name and description in other languages, keyed by language code. Absent when nothing is translated; a language or field missing here falls back to `name` / `description`. |
es | object | |
name | string | |
description | string | |
en | object | |
name | string | |
description | string | |
pt | object | |
name | string | |
description | string | |
fr | object | |
name | string | |
description | string | |
de | object | |
name | string | |
description | string | |
it | object | |
name | string | |
description | string | |
Erros
400 VALIDATION — a field is missing or malformed; `issues` names it401 UNAUTHORIZED — the key is missing, unknown or revoked402 NOT_PREMIUM — the restaurant has no active subscription or trial403 FORBIDDEN — the key lacks the scope this endpoint needs404 NOT_FOUND — no such restaurant in this account, or no such item in it500 INTERNAL — something failed on our side; retry in a moment
delete/restaurants/{ref}/products/{id}/imagerequer menu:writeremoveProductImage
Remove the product photo
Parâmetros
| Nome | Onde | Descrição |
|---|
ref* | caminho | The restaurant identifier (the slug in its public URL, e.g. "pizzeria-roma") or its id, as returned by GET /restaurants |
id* | caminho | The product id, from GET /restaurants/{ref}/products |
Exemplo
curl -X DELETE "https://delimenu.co/api/v1/restaurants/pizzeria-roma/products/p9Hs4TqL2mNc8VbX6Rdy/image" \
-H "Authorization: Bearer dmk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
Resposta 200
{
"product_id": "p9Hs4TqL2mNc8VbX6Rdy",
"has_image": false
}
Campos da resposta
| Campo | Tipo | Descrição |
|---|
product_id* | string | |
has_image* | false | |
Erros
401 UNAUTHORIZED — the key is missing, unknown or revoked402 NOT_PREMIUM — the restaurant has no active subscription or trial403 FORBIDDEN — the key lacks the scope this endpoint needs404 NOT_FOUND — no such restaurant in this account, or no such item in it500 INTERNAL — something failed on our side; retry in a moment
Webhooks
Get notified when a menu changes instead of polling it. Events: product.created, product.updated, product.deleted, category.created, category.updated, category.deleted, restaurant.updated, restaurant.deleted. Each is a POST with a JSON body { id, type, api_version: "2026-10-01", created_at, restaurant: { id, identifier }, data: { object, changes? } }: object is the product, category or restaurant as the API returns it (categories add product_ids and restaurants add category_ids, in display order; a deleted item carries its last state), and changes lists the top-level fields an update touched. Writes that change nothing visible send nothing. Verify every request: the Delimenu-Signature header is t=<unix seconds>,v1=<hex>, where v1 is HMAC-SHA256 of "<t>.<raw body>" with the endpoint secret; compare in constant time and reject a t older than 5 minutes. Answer any 2xx within 10 seconds and do the work afterwards. Anything else is retried with exponential backoff, up to 8 attempts over about a day, so deduplicate by id (also in the Delimenu-Event-Id header). An endpoint that exhausts every retry on 20 events in a row is switched off. Only restaurants with an active subscription or trial send events.
get/restaurants/{ref}/webhooksrequer menu:readlistWebhooks
List the webhooks of a restaurant
Signing secrets are never returned here; only when an endpoint is created or its secret rotated.
Parâmetros
| Nome | Onde | Descrição |
|---|
ref* | caminho | The restaurant identifier (the slug in its public URL, e.g. "pizzeria-roma") or its id, as returned by GET /restaurants |
Exemplo
curl -X GET "https://delimenu.co/api/v1/restaurants/pizzeria-roma/webhooks" \
-H "Authorization: Bearer dmk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
Resposta 200
{
"webhooks": [
{
"id": "w7Kd2PqR9sLm4XvB1nTc",
"url": "https://pos.example.com/delimenu/webhooks",
"events": [
"product.created",
"product.updated",
"product.deleted"
],
"enabled": true,
"disabled_reason": null,
"created_at": "2026-10-01T15:04:05.000Z",
"last_delivery_at": "2026-10-02T09:30:00.000Z",
"last_status": 200
}
]
}
Campos da resposta
| Campo | Tipo | Descrição |
|---|
webhooks* | object[] | |
id* | string | |
url* | string | |
events* | "product.created" | "product.updated" | "product.deleted" | "category.created" | "category.updated" | "category.deleted" | "restaurant.updated" | "restaurant.deleted"[] | |
enabled* | boolean | |
disabled_reason* | "failing" | "manual" | "failing" when it was switched off for failing too many events in a row |
created_at* | string | null | |
last_delivery_at* | string | null | |
last_status* | integer | null | HTTP status of the last attempt; null if it never answeredmin -9007199254740991 · max 9007199254740991 |
Erros
401 UNAUTHORIZED — the key is missing, unknown or revoked402 NOT_PREMIUM — the restaurant has no active subscription or trial403 FORBIDDEN — the key lacks the scope this endpoint needs404 NOT_FOUND — no such restaurant in this account, or no such item in it500 INTERNAL — something failed on our side; retry in a moment
post/restaurants/{ref}/webhooksrequer menu:writecreateWebhook
Register a webhook
Starts sending the chosen events to the URL. The response carries the signing secret, the only time it is shown: store it and use it to verify Delimenu-Signature. Up to 5 per restaurant; one URL once per restaurant.
Parâmetros
| Nome | Onde | Descrição |
|---|
ref* | caminho | The restaurant identifier (the slug in its public URL, e.g. "pizzeria-roma") or its id, as returned by GET /restaurants |
Corpo (JSON)
| Campo | Tipo | Descrição |
|---|
url* | string | Where the events are POSTed: https, a public domain, port 443até 2048 caracteres |
events* | "product.created" | "product.updated" | "product.deleted" | "category.created" | "category.updated" | "category.deleted" | "restaurant.updated" | "restaurant.deleted"[] | The events this endpoint receives |
Exemplo
curl -X POST "https://delimenu.co/api/v1/restaurants/pizzeria-roma/webhooks" \
-H "Authorization: Bearer dmk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{"url":"https://pos.example.com/delimenu/webhooks","events":["product.created","product.updated","product.deleted"]}'
Resposta 201
{
"webhook": {
"id": "w7Kd2PqR9sLm4XvB1nTc",
"url": "https://pos.example.com/delimenu/webhooks",
"events": [
"product.created",
"product.updated",
"product.deleted"
],
"enabled": true,
"disabled_reason": null,
"created_at": "2026-10-01T15:04:05.000Z",
"last_delivery_at": null,
"last_status": null
},
"secret": "whsec_Zx8Qm2Lk5Vb9Tn3Rw7Yc1Hd4Fg6Js0Pa"
}
Campos da resposta
| Campo | Tipo | Descrição |
|---|
webhook* | object | |
id* | string | |
url* | string | |
events* | "product.created" | "product.updated" | "product.deleted" | "category.created" | "category.updated" | "category.deleted" | "restaurant.updated" | "restaurant.deleted"[] | |
enabled* | boolean | |
disabled_reason* | "failing" | "manual" | "failing" when it was switched off for failing too many events in a row |
created_at* | string | null | |
last_delivery_at* | string | null | |
last_status* | integer | null | HTTP status of the last attempt; null if it never answeredmin -9007199254740991 · max 9007199254740991 |
secret* | string | The signing secret. Shown only in this response; store it now |
Erros
400 VALIDATION — a field is missing or malformed; `issues` names it401 UNAUTHORIZED — the key is missing, unknown or revoked402 NOT_PREMIUM — the restaurant has no active subscription or trial403 FORBIDDEN — the key lacks the scope this endpoint needs404 NOT_FOUND — no such restaurant in this account, or no such item in it409 CONFLICT — this restaurant already has a webhook with that URL500 INTERNAL — something failed on our side; retry in a moment
patch/restaurants/{ref}/webhooks/{id}requer menu:writeupdateWebhook
Update a webhook
Changes only the fields given. enabled: true turns back on an endpoint that was switched off for failing, and clears its failure count.
Parâmetros
| Nome | Onde | Descrição |
|---|
ref* | caminho | The restaurant identifier (the slug in its public URL, e.g. "pizzeria-roma") or its id, as returned by GET /restaurants |
id* | caminho | The webhook id, from GET /restaurants/{ref}/webhooks |
Corpo (JSON)
| Campo | Tipo | Descrição |
|---|
url | string | Where the events are POSTed: https, a public domain, port 443até 2048 caracteres |
events | "product.created" | "product.updated" | "product.deleted" | "category.created" | "category.updated" | "category.deleted" | "restaurant.updated" | "restaurant.deleted"[] | The events this endpoint receives |
enabled | boolean | false stops deliveries; true turns a switched-off endpoint back on and clears its failure count |
Exemplo
curl -X PATCH "https://delimenu.co/api/v1/restaurants/pizzeria-roma/webhooks/w7Kd2PqR9sLm4XvB1nTc" \
-H "Authorization: Bearer dmk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{"enabled":true}'
Resposta 200
{
"webhook": {
"id": "w7Kd2PqR9sLm4XvB1nTc",
"url": "https://pos.example.com/delimenu/webhooks",
"events": [
"product.created",
"product.updated",
"product.deleted"
],
"enabled": true,
"disabled_reason": null,
"created_at": "2026-10-01T15:04:05.000Z",
"last_delivery_at": "2026-10-02T09:30:00.000Z",
"last_status": 200
}
}
Campos da resposta
| Campo | Tipo | Descrição |
|---|
webhook* | object | |
id* | string | |
url* | string | |
events* | "product.created" | "product.updated" | "product.deleted" | "category.created" | "category.updated" | "category.deleted" | "restaurant.updated" | "restaurant.deleted"[] | |
enabled* | boolean | |
disabled_reason* | "failing" | "manual" | "failing" when it was switched off for failing too many events in a row |
created_at* | string | null | |
last_delivery_at* | string | null | |
last_status* | integer | null | HTTP status of the last attempt; null if it never answeredmin -9007199254740991 · max 9007199254740991 |
Erros
400 VALIDATION — a field is missing or malformed; `issues` names it401 UNAUTHORIZED — the key is missing, unknown or revoked402 NOT_PREMIUM — the restaurant has no active subscription or trial403 FORBIDDEN — the key lacks the scope this endpoint needs404 NOT_FOUND — no such restaurant in this account, or no such item in it409 CONFLICT — this restaurant already has a webhook with that URL500 INTERNAL — something failed on our side; retry in a moment
delete/restaurants/{ref}/webhooks/{id}requer menu:writedeleteWebhook
Delete a webhook
Stops every delivery at once, retries already queued included.
Parâmetros
| Nome | Onde | Descrição |
|---|
ref* | caminho | The restaurant identifier (the slug in its public URL, e.g. "pizzeria-roma") or its id, as returned by GET /restaurants |
id* | caminho | The webhook id, from GET /restaurants/{ref}/webhooks |
Exemplo
curl -X DELETE "https://delimenu.co/api/v1/restaurants/pizzeria-roma/webhooks/w7Kd2PqR9sLm4XvB1nTc" \
-H "Authorization: Bearer dmk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
Resposta 200
{
"deleted": "w7Kd2PqR9sLm4XvB1nTc"
}
Campos da resposta
| Campo | Tipo | Descrição |
|---|
deleted* | string | |
Erros
401 UNAUTHORIZED — the key is missing, unknown or revoked402 NOT_PREMIUM — the restaurant has no active subscription or trial403 FORBIDDEN — the key lacks the scope this endpoint needs404 NOT_FOUND — no such restaurant in this account, or no such item in it500 INTERNAL — something failed on our side; retry in a moment
post/restaurants/{ref}/webhooks/{id}/secretrequer menu:writerotateWebhookSecret
Rotate the signing secret
Replaces the secret; the old one stops verifying on the very next delivery. The response is the only time the new one is shown.
Parâmetros
| Nome | Onde | Descrição |
|---|
ref* | caminho | The restaurant identifier (the slug in its public URL, e.g. "pizzeria-roma") or its id, as returned by GET /restaurants |
id* | caminho | The webhook id, from GET /restaurants/{ref}/webhooks |
Exemplo
curl -X POST "https://delimenu.co/api/v1/restaurants/pizzeria-roma/webhooks/w7Kd2PqR9sLm4XvB1nTc/secret" \
-H "Authorization: Bearer dmk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
Resposta 200
{
"webhook": {
"id": "w7Kd2PqR9sLm4XvB1nTc",
"url": "https://pos.example.com/delimenu/webhooks",
"events": [
"product.created",
"product.updated",
"product.deleted"
],
"enabled": true,
"disabled_reason": null,
"created_at": "2026-10-01T15:04:05.000Z",
"last_delivery_at": "2026-10-02T09:30:00.000Z",
"last_status": 200
},
"secret": "whsec_Zx8Qm2Lk5Vb9Tn3Rw7Yc1Hd4Fg6Js0Pa"
}
Campos da resposta
| Campo | Tipo | Descrição |
|---|
webhook* | object | |
id* | string | |
url* | string | |
events* | "product.created" | "product.updated" | "product.deleted" | "category.created" | "category.updated" | "category.deleted" | "restaurant.updated" | "restaurant.deleted"[] | |
enabled* | boolean | |
disabled_reason* | "failing" | "manual" | "failing" when it was switched off for failing too many events in a row |
created_at* | string | null | |
last_delivery_at* | string | null | |
last_status* | integer | null | HTTP status of the last attempt; null if it never answeredmin -9007199254740991 · max 9007199254740991 |
secret* | string | The signing secret. Shown only in this response; store it now |
Erros
401 UNAUTHORIZED — the key is missing, unknown or revoked402 NOT_PREMIUM — the restaurant has no active subscription or trial403 FORBIDDEN — the key lacks the scope this endpoint needs404 NOT_FOUND — no such restaurant in this account, or no such item in it500 INTERNAL — something failed on our side; retry in a moment
post/restaurants/{ref}/webhooks/{id}/testrequer menu:writetestWebhook
Send a test event
POSTs a signed "ping" event now and returns how the endpoint answered. A failing endpoint is still a 200 here, with ok: false. Works on a switched-off endpoint too.
Parâmetros
| Nome | Onde | Descrição |
|---|
ref* | caminho | The restaurant identifier (the slug in its public URL, e.g. "pizzeria-roma") or its id, as returned by GET /restaurants |
id* | caminho | The webhook id, from GET /restaurants/{ref}/webhooks |
Exemplo
curl -X POST "https://delimenu.co/api/v1/restaurants/pizzeria-roma/webhooks/w7Kd2PqR9sLm4XvB1nTc/test" \
-H "Authorization: Bearer dmk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
Resposta 200
{
"delivery": {
"id": "d4Nf8Kq2Rm6Xs1Vb9Lt3",
"event_id": "evt_test_8c1e4a7f2b9d3e6a0c5f",
"type": "ping",
"attempt": 1,
"ok": true,
"http_status": 200,
"duration_ms": 184,
"error": null,
"response_excerpt": "{\"received\":true}",
"created_at": "2026-10-02T09:30:00.000Z"
}
}
Campos da resposta
| Campo | Tipo | Descrição |
|---|
delivery* | object | |
id* | string | |
event_id* | string | |
type* | string | |
attempt* | integer | 1 for the first try, then each retrymin -9007199254740991 · max 9007199254740991 |
ok* | boolean | |
http_status* | integer | null | min -9007199254740991 · max 9007199254740991 |
duration_ms* | integer | min -9007199254740991 · max 9007199254740991 |
error* | string | null | "timeout", a network error, or null when the endpoint answered |
response_excerpt* | string | null | |
created_at* | string | null | |
Erros
401 UNAUTHORIZED — the key is missing, unknown or revoked402 NOT_PREMIUM — the restaurant has no active subscription or trial403 FORBIDDEN — the key lacks the scope this endpoint needs404 NOT_FOUND — no such restaurant in this account, or no such item in it500 INTERNAL — something failed on our side; retry in a moment
get/restaurants/{ref}/webhooks/{id}/deliveriesrequer menu:readlistWebhookDeliveries
Latest delivery attempts
The most recent attempts to this endpoint, newest first, retries included. Kept for 14 days.
Parâmetros
| Nome | Onde | Descrição |
|---|
ref* | caminho | The restaurant identifier (the slug in its public URL, e.g. "pizzeria-roma") or its id, as returned by GET /restaurants |
id* | caminho | The webhook id, from GET /restaurants/{ref}/webhooks |
Exemplo
curl -X GET "https://delimenu.co/api/v1/restaurants/pizzeria-roma/webhooks/w7Kd2PqR9sLm4XvB1nTc/deliveries" \
-H "Authorization: Bearer dmk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
Resposta 200
{
"deliveries": [
{
"id": "d4Nf8Kq2Rm6Xs1Vb9Lt3",
"event_id": "evt_3f9a1c7e5b2d8a4c6e0f1b3d",
"type": "product.updated",
"attempt": 1,
"ok": true,
"http_status": 200,
"duration_ms": 184,
"error": null,
"response_excerpt": "{\"received\":true}",
"created_at": "2026-10-02T09:30:00.000Z"
}
]
}
Campos da resposta
| Campo | Tipo | Descrição |
|---|
deliveries* | object[] | |
id* | string | |
event_id* | string | |
type* | string | |
attempt* | integer | 1 for the first try, then each retrymin -9007199254740991 · max 9007199254740991 |
ok* | boolean | |
http_status* | integer | null | min -9007199254740991 · max 9007199254740991 |
duration_ms* | integer | min -9007199254740991 · max 9007199254740991 |
error* | string | null | "timeout", a network error, or null when the endpoint answered |
response_excerpt* | string | null | |
created_at* | string | null | |
Erros
401 UNAUTHORIZED — the key is missing, unknown or revoked402 NOT_PREMIUM — the restaurant has no active subscription or trial403 FORBIDDEN — the key lacks the scope this endpoint needs404 NOT_FOUND — no such restaurant in this account, or no such item in it500 INTERNAL — something failed on our side; retry in a moment