EntrarCadastrar-se
    EntrarCadastrar-se
    EspañolEnglishPortuguês

    API para desenvolvedores

    Gerencie seu cardápio a partir do seu próprio sistema, ou exiba-o em um frontend feito sob medida.

    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

    1. 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.
    2. 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.
    3. 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.

    HTTPerrorQuando
    400VALIDATIONFalta um campo ou ele tem um formato inválido. `issues` diz qual.
    401UNAUTHORIZEDA chave está ausente, não existe ou foi revogada.
    402NOT_PREMIUMO restaurante não tem assinatura nem teste ativo.
    403FORBIDDENA chave não tem a permissão que o endpoint exige.
    404NOT_FOUNDO restaurante não está na sua conta, ou o produto ou a categoria não existem nele.
    409CONFLICTNome de categoria repetido, identificador já em uso ou categoria com produtos.
    422LIMITUm limite foi atingido.
    500INTERNALAlgo 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.

    get/menus/{identifier}públicogetPublicMenu

    Public menu of a restaurant

    The categories with at least one visible product, in display order, each with its visible products in display order. Hidden products and empty categories are never included. restaurant.language is the language name and description are written in, and restaurant.languages the ones the menu is translated into: to render one of those, read translations[code] on each category, product, variant and option and fall back to the original field when it is missing. Cached at the edge and refreshed on every change the owner makes; a differently-cased identifier is redirected to the lowercase one.

    Parâmetros

    NomeOndeDescrição
    identifier*caminhoThe slug in the public URL: delimenu.co/{identifier}

    Exemplo

    curl -X GET "https://delimenu.co/api/v1/menus/pizzeria-roma"

    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"
        ]
      },
      "categories": [
        {
          "id": "k3Qm8vXb2LpN7wRt1YaZ",
          "name": "Pizzas",
          "position": 1,
          "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
                    }
                  ]
                }
              ]
            }
          ]
        }
      ]
    }

    Campos da resposta

    CampoTipoDescrição
    restaurant*object
    id*string
    identifier*string
    name*string
    currency*stringISO 4217 code every price is in
    type*"whatsapp" | "read_only""whatsapp" takes orders by WhatsApp; "read_only" only shows the menu
    phone*string | nullWhatsApp 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.
    categories*object[]Categories with at least one visible product, in display order
    id*string
    name*string
    translationsobjectThe 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`.
    esobject
    namestring
    enobject
    namestring
    ptobject
    namestring
    frobject
    namestring
    deobject
    namestring
    itobject
    namestring
    position*integer1-based display positionmin -9007199254740991 · max 9007199254740991
    products*object[]
    id*string
    category_id*string
    name*string
    price*numberMajor units in the restaurant's currency
    original_pricenumberPresent while the product is on promotion
    descriptionstring
    available*booleanfalse renders "Sin stock"
    hidden*booleantrue keeps it off the public menu
    has_image*boolean
    image_urlstring
    variants*object[]
    id*string
    name*string
    min_selections*integermin -9007199254740991 · max 9007199254740991
    max_selections*integer0 is no limitmin -9007199254740991 · max 9007199254740991
    max_per_optionintegermin -9007199254740991 · max 9007199254740991
    options*object[]
    translationsobjectThe 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`.
    translationsobjectName 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`.
    esobject
    enobject
    ptobject
    frobject
    deobject
    itobject

    Erros

    • 403 RESTAURANT_UNAVAILABLE — the restaurant exists but has no active plan; only its name is returned
    • 404 NOT_FOUND — no such restaurant in this account, or no such item in it
    • 500 INTERNAL — something failed on our side; retry in a moment

    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

    CampoTipoDescrição
    restaurants*object[]
    id*string
    identifier*string
    name*string
    currency*stringISO 4217 code every price is in
    type*"whatsapp" | "read_only""whatsapp" takes orders by WhatsApp; "read_only" only shows the menu
    phone*string | nullWhatsApp 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 revoked
    • 402 NOT_PREMIUM — the restaurant has no active subscription or trial
    • 403 FORBIDDEN — the key lacks the scope this endpoint needs
    • 404 NOT_FOUND — no such restaurant in this account, or no such item in it
    • 500 INTERNAL — something failed on our side; retry in a moment
    get/restaurants/{ref}requer menu:readgetRestaurant

    Get a restaurant

    Parâmetros

    NomeOndeDescrição
    ref*caminhoThe 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

    CampoTipoDescrição
    restaurant*object
    id*string
    identifier*string
    name*string
    currency*stringISO 4217 code every price is in
    type*"whatsapp" | "read_only""whatsapp" takes orders by WhatsApp; "read_only" only shows the menu
    phone*string | nullWhatsApp 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 revoked
    • 402 NOT_PREMIUM — the restaurant has no active subscription or trial
    • 403 FORBIDDEN — the key lacks the scope this endpoint needs
    • 404 NOT_FOUND — no such restaurant in this account, or no such item in it
    • 500 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

    NomeOndeDescrição
    ref*caminhoThe restaurant identifier (the slug in its public URL, e.g. "pizzeria-roma") or its id, as returned by GET /restaurants

    Corpo (JSON)

    CampoTipoDescrição
    namestringaté 50 caracteres
    phonestringWhatsApp number that receives orders, in E.164 format (+573001234567)
    addressstringStreet address shown on the menu. Empty string removes it.até 65 caracteres
    currencystringISO 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

    CampoTipoDescrição
    restaurant*object
    id*string
    identifier*string
    name*string
    currency*stringISO 4217 code every price is in
    type*"whatsapp" | "read_only""whatsapp" takes orders by WhatsApp; "read_only" only shows the menu
    phone*string | nullWhatsApp 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 it
    • 401 UNAUTHORIZED — the key is missing, unknown or revoked
    • 402 NOT_PREMIUM — the restaurant has no active subscription or trial
    • 403 FORBIDDEN — the key lacks the scope this endpoint needs
    • 404 NOT_FOUND — no such restaurant in this account, or no such item in it
    • 500 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

    NomeOndeDescrição
    ref*caminhoThe restaurant identifier (the slug in its public URL, e.g. "pizzeria-roma") or its id, as returned by GET /restaurants

    Corpo (JSON)

    CampoTipoDescrição
    identifier*stringThe 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

    CampoTipoDescrição
    restaurant*object
    id*string
    identifier*string
    name*string
    currency*stringISO 4217 code every price is in
    type*"whatsapp" | "read_only""whatsapp" takes orders by WhatsApp; "read_only" only shows the menu
    phone*string | nullWhatsApp 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 it
    • 401 UNAUTHORIZED — the key is missing, unknown or revoked
    • 402 NOT_PREMIUM — the restaurant has no active subscription or trial
    • 403 FORBIDDEN — the key lacks the scope this endpoint needs
    • 404 NOT_FOUND — no such restaurant in this account, or no such item in it
    • 409 CONFLICT — the identifier is already taken
    • 500 INTERNAL — something failed on our side; retry in a moment
    get/restaurants/{ref}/menurequer menu:readgetMenu

    Get the whole menu

    Every category in display order, each with its products in display order, prices, availability and variants. Hidden products are omitted unless include_hidden is true. Uncategorized products are listed apart.

    Parâmetros

    NomeOndeDescrição
    ref*caminhoThe restaurant identifier (the slug in its public URL, e.g. "pizzeria-roma") or its id, as returned by GET /restaurants
    include_hiddenqueryAlso return the products hidden from the public menu

    Exemplo

    curl -X GET "https://delimenu.co/api/v1/restaurants/pizzeria-roma/menu?include_hidden=true" \
      -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
      },
      "categories": [
        {
          "id": "k3Qm8vXb2LpN7wRt1YaZ",
          "name": "Pizzas",
          "position": 1,
          "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
                    }
                  ]
                }
              ]
            }
          ]
        }
      ],
      "uncategorized_products": [],
      "hidden_products_omitted": 2,
      "note": "Prices are in COP, major units."
    }

    Campos da resposta

    CampoTipoDescrição
    restaurant*object
    id*string
    identifier*string
    name*string
    currency*stringISO 4217 code every price is in
    type*"whatsapp" | "read_only""whatsapp" takes orders by WhatsApp; "read_only" only shows the menu
    phone*string | nullWhatsApp 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
    categories*object[]
    id*string
    name*string
    translationsobjectThe 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`.
    esobject
    namestring
    enobject
    namestring
    ptobject
    namestring
    frobject
    namestring
    deobject
    namestring
    itobject
    namestring
    position*integer1-based display positionmin -9007199254740991 · max 9007199254740991
    products*object[]
    id*string
    category_id*string
    name*string
    price*numberMajor units in the restaurant's currency
    original_pricenumberPresent while the product is on promotion
    descriptionstring
    available*booleanfalse renders "Sin stock"
    hidden*booleantrue keeps it off the public menu
    has_image*boolean
    image_urlstring
    variants*object[]
    id*string
    name*string
    min_selections*integermin -9007199254740991 · max 9007199254740991
    max_selections*integer0 is no limitmin -9007199254740991 · max 9007199254740991
    max_per_optionintegermin -9007199254740991 · max 9007199254740991
    options*object[]
    translationsobjectThe 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`.
    translationsobjectName 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`.
    esobject
    enobject
    ptobject
    frobject
    deobject
    itobject
    uncategorized_products*object[]
    id*string
    category_id*string
    name*string
    price*numberMajor units in the restaurant's currency
    original_pricenumberPresent while the product is on promotion
    descriptionstring
    available*booleanfalse renders "Sin stock"
    hidden*booleantrue keeps it off the public menu
    has_image*boolean
    image_urlstring
    variants*object[]
    id*string
    name*string
    min_selections*integermin -9007199254740991 · max 9007199254740991
    max_selections*integer0 is no limitmin -9007199254740991 · max 9007199254740991
    max_per_optionintegermin -9007199254740991 · max 9007199254740991
    options*object[]
    id*string
    name*string
    price*numberExtra charge on top of the product price
    show*boolean
    translationsobjectThe 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`.
    translationsobjectThe 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`.
    esobject
    enobject
    ptobject
    frobject
    deobject
    itobject
    translationsobjectName 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`.
    esobject
    namestring
    descriptionstring
    enobject
    namestring
    descriptionstring
    ptobject
    namestring
    descriptionstring
    frobject
    namestring
    descriptionstring
    deobject
    namestring
    descriptionstring
    itobject
    namestring
    descriptionstring
    hidden_products_omitted*integermin -9007199254740991 · max 9007199254740991
    note*string

    Erros

    • 400 VALIDATION — a field is missing or malformed; `issues` names it
    • 401 UNAUTHORIZED — the key is missing, unknown or revoked
    • 402 NOT_PREMIUM — the restaurant has no active subscription or trial
    • 403 FORBIDDEN — the key lacks the scope this endpoint needs
    • 404 NOT_FOUND — no such restaurant in this account, or no such item in it
    • 500 INTERNAL — something failed on our side; retry in a moment

    Categories

    get/restaurants/{ref}/categoriesrequer menu:readlistCategories

    List the categories

    Parâmetros

    NomeOndeDescrição
    ref*caminhoThe 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

    CampoTipoDescrição
    categories*object[]
    id*string
    name*string
    translationsobjectThe 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`.
    esobject
    namestring
    enobject
    namestring
    ptobject
    namestring
    frobject
    namestring
    deobject
    namestring
    itobject
    namestring
    position*integermin -9007199254740991 · max 9007199254740991

    Erros

    • 401 UNAUTHORIZED — the key is missing, unknown or revoked
    • 402 NOT_PREMIUM — the restaurant has no active subscription or trial
    • 403 FORBIDDEN — the key lacks the scope this endpoint needs
    • 404 NOT_FOUND — no such restaurant in this account, or no such item in it
    • 500 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

    NomeOndeDescrição
    ref*caminhoThe restaurant identifier (the slug in its public URL, e.g. "pizzeria-roma") or its id, as returned by GET /restaurants

    Corpo (JSON)

    CampoTipoDescrição
    name*stringCategory nameaté 50 caracteres
    translationsobjectThe 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.
    esobject
    namestringThe name in that languageaté 50 caracteres
    enobject
    namestringThe name in that languageaté 50 caracteres
    ptobject
    namestringThe name in that languageaté 50 caracteres
    frobject
    namestringThe name in that languageaté 50 caracteres
    deobject
    namestringThe name in that languageaté 50 caracteres
    itobject
    namestringThe 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

    CampoTipoDescrição
    category*object
    id*string
    name*string
    translationsobjectThe 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`.
    esobject
    namestring
    enobject
    namestring
    ptobject
    namestring
    frobject
    namestring
    deobject
    namestring
    itobject
    namestring

    Erros

    • 400 VALIDATION — a field is missing or malformed; `issues` names it
    • 401 UNAUTHORIZED — the key is missing, unknown or revoked
    • 402 NOT_PREMIUM — the restaurant has no active subscription or trial
    • 403 FORBIDDEN — the key lacks the scope this endpoint needs
    • 404 NOT_FOUND — no such restaurant in this account, or no such item in it
    • 409 CONFLICT — a category with that name already exists
    • 500 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

    NomeOndeDescrição
    ref*caminhoThe restaurant identifier (the slug in its public URL, e.g. "pizzeria-roma") or its id, as returned by GET /restaurants

    Corpo (JSON)

    CampoTipoDescriçã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

    CampoTipoDescrição
    order_categories*string[]

    Erros

    • 400 VALIDATION — a field is missing or malformed; `issues` names it
    • 401 UNAUTHORIZED — the key is missing, unknown or revoked
    • 402 NOT_PREMIUM — the restaurant has no active subscription or trial
    • 403 FORBIDDEN — the key lacks the scope this endpoint needs
    • 404 NOT_FOUND — no such restaurant in this account, or no such item in it
    • 500 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

    NomeOndeDescrição
    ref*caminhoThe restaurant identifier (the slug in its public URL, e.g. "pizzeria-roma") or its id, as returned by GET /restaurants
    id*caminhoThe category id, from GET /restaurants/{ref}/categories

    Corpo (JSON)

    CampoTipoDescrição
    namestringCategory nameaté 50 caracteres
    translationsobjectThe 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.
    esobject
    namestringThe name in that languageaté 50 caracteres
    enobject
    namestringThe name in that languageaté 50 caracteres
    ptobject
    namestringThe name in that languageaté 50 caracteres
    frobject
    namestringThe name in that languageaté 50 caracteres
    deobject
    namestringThe name in that languageaté 50 caracteres
    itobject
    namestringThe 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

    CampoTipoDescrição
    category*object
    id*string
    name*string
    translationsobjectThe 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`.
    esobject
    namestring
    enobject
    namestring
    ptobject
    namestring
    frobject
    namestring
    deobject
    namestring
    itobject
    namestring

    Erros

    • 400 VALIDATION — a field is missing or malformed; `issues` names it
    • 401 UNAUTHORIZED — the key is missing, unknown or revoked
    • 402 NOT_PREMIUM — the restaurant has no active subscription or trial
    • 403 FORBIDDEN — the key lacks the scope this endpoint needs
    • 404 NOT_FOUND — no such restaurant in this account, or no such item in it
    • 500 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

    NomeOndeDescrição
    ref*caminhoThe restaurant identifier (the slug in its public URL, e.g. "pizzeria-roma") or its id, as returned by GET /restaurants
    id*caminhoThe category id, from GET /restaurants/{ref}/categories
    delete_productsqueryDelete 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

    CampoTipoDescrição
    deleted*string
    deleted_products*integermin -9007199254740991 · max 9007199254740991

    Erros

    • 400 VALIDATION — a field is missing or malformed; `issues` names it
    • 401 UNAUTHORIZED — the key is missing, unknown or revoked
    • 402 NOT_PREMIUM — the restaurant has no active subscription or trial
    • 403 FORBIDDEN — the key lacks the scope this endpoint needs
    • 404 NOT_FOUND — no such restaurant in this account, or no such item in it
    • 409 CONFLICT — the category still has products
    • 500 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

    NomeOndeDescrição
    ref*caminhoThe restaurant identifier (the slug in its public URL, e.g. "pizzeria-roma") or its id, as returned by GET /restaurants
    id*caminhoThe category id, from GET /restaurants/{ref}/categories

    Corpo (JSON)

    CampoTipoDescriçã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

    CampoTipoDescrição
    category_id*string
    order_products*string[]

    Erros

    • 400 VALIDATION — a field is missing or malformed; `issues` names it
    • 401 UNAUTHORIZED — the key is missing, unknown or revoked
    • 402 NOT_PREMIUM — the restaurant has no active subscription or trial
    • 403 FORBIDDEN — the key lacks the scope this endpoint needs
    • 404 NOT_FOUND — no such restaurant in this account, or no such item in it
    • 500 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

    NomeOndeDescrição
    ref*caminhoThe restaurant identifier (the slug in its public URL, e.g. "pizzeria-roma") or its id, as returned by GET /restaurants
    qqueryOnly 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

    CampoTipoDescrição
    products*object[]
    id*string
    category_id*string
    name*string
    price*numberMajor units in the restaurant's currency
    original_pricenumberPresent while the product is on promotion
    descriptionstring
    available*booleanfalse renders "Sin stock"
    hidden*booleantrue keeps it off the public menu
    has_image*boolean
    image_urlstring
    variants*object[]
    id*string
    name*string
    min_selections*integermin -9007199254740991 · max 9007199254740991
    max_selections*integer0 is no limitmin -9007199254740991 · max 9007199254740991
    max_per_optionintegermin -9007199254740991 · max 9007199254740991
    options*object[]
    id*string
    name*string
    price*numberExtra charge on top of the product price
    show*boolean
    translationsobjectThe 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`.
    translationsobjectThe 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`.
    esobject
    enobject
    ptobject
    frobject
    deobject
    itobject
    translationsobjectName 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`.
    esobject
    namestring
    descriptionstring
    enobject
    namestring
    descriptionstring
    ptobject
    namestring
    descriptionstring
    frobject
    namestring
    descriptionstring
    deobject
    namestring
    descriptionstring
    itobject
    namestring
    descriptionstring
    count*integermin -9007199254740991 · max 9007199254740991

    Erros

    • 400 VALIDATION — a field is missing or malformed; `issues` names it
    • 401 UNAUTHORIZED — the key is missing, unknown or revoked
    • 402 NOT_PREMIUM — the restaurant has no active subscription or trial
    • 403 FORBIDDEN — the key lacks the scope this endpoint needs
    • 404 NOT_FOUND — no such restaurant in this account, or no such item in it
    • 500 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

    NomeOndeDescrição
    ref*caminhoThe restaurant identifier (the slug in its public URL, e.g. "pizzeria-roma") or its id, as returned by GET /restaurants

    Corpo (JSON)

    CampoTipoDescrição
    category_id*stringThe category the product belongs to (from get_menu)até 128 caracteres
    name*stringProduct nameaté 50 caracteres
    price*numberPrice in the restaurant's currency, in major units (12500 for COP $12.000, 5.99 for USD $5.99). Never cents.min 0
    descriptionstringOptional description. Pass an empty string to remove it.até 3000 caracteres
    original_pricenumber | nullThe price before a promotion. Must be greater than price; the menu shows it struck through. Pass null to end the promotion.min 0
    availablebooleanfalse shows the product as "Sin stock": visible but not orderable. Default true.
    hiddenbooleantrue keeps the product off the public menu entirely. Default false.
    variantsobject[]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*stringVariant name, e.g. "Tamaño" or "Adiciones"até 50 caracteres
    translationsobjectThe 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.
    esobject
    namestringThe name in that languageaté 50 caracteres
    enobject
    namestringThe name in that languageaté 50 caracteres
    ptobject
    namestringThe name in that languageaté 50 caracteres
    frobject
    namestringThe name in that languageaté 50 caracteres
    deobject
    namestringThe name in that languageaté 50 caracteres
    itobject
    namestringThe name in that languageaté 50 caracteres
    min_selectionsintegerOptions the diner must pick. 0 makes the variant optional.min 0 · max 9007199254740991 · padrão 0
    max_selectionsintegerOptions the diner may pick. 0 is no limit; 1 renders a single choice.min 0 · max 9007199254740991 · padrão 0
    max_per_optionintegerHow many times one option may be repeated. Omit or 0 for no limit.min 0 · max 9007199254740991
    options*object[]
    name*stringOption name, e.g. "Grande"até 50 caracteres
    translationsobjectThe 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.
    esobject
    enobject
    ptobject
    frobject
    deobject
    itobject
    pricenumberExtra charge for this option on top of the product price. 0 when it costs nothing.min 0 · padrão 0
    showbooleanfalse hides the option from diners without deleting itpadrão true
    translationsobjectThe 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.
    esobject
    namestringThe product name in that languageaté 50 caracteres
    descriptionstringThe description in that languageaté 3000 caracteres
    enobject
    namestringThe product name in that languageaté 50 caracteres
    descriptionstringThe description in that languageaté 3000 caracteres
    ptobject
    namestringThe product name in that languageaté 50 caracteres
    descriptionstringThe description in that languageaté 3000 caracteres
    frobject
    namestringThe product name in that languageaté 50 caracteres
    descriptionstringThe description in that languageaté 3000 caracteres
    deobject
    namestringThe product name in that languageaté 50 caracteres
    descriptionstringThe description in that languageaté 3000 caracteres
    itobject
    namestringThe product name in that languageaté 50 caracteres
    descriptionstringThe 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

    CampoTipoDescrição
    product*object
    id*string
    category_id*string
    name*string
    price*numberMajor units in the restaurant's currency
    original_pricenumberPresent while the product is on promotion
    descriptionstring
    available*booleanfalse renders "Sin stock"
    hidden*booleantrue keeps it off the public menu
    has_image*boolean
    image_urlstring
    variants*object[]
    id*string
    name*string
    min_selections*integermin -9007199254740991 · max 9007199254740991
    max_selections*integer0 is no limitmin -9007199254740991 · max 9007199254740991
    max_per_optionintegermin -9007199254740991 · max 9007199254740991
    options*object[]
    id*string
    name*string
    price*numberExtra charge on top of the product price
    show*boolean
    translationsobjectThe 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`.
    translationsobjectThe 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`.
    esobject
    enobject
    ptobject
    frobject
    deobject
    itobject
    translationsobjectName 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`.
    esobject
    namestring
    descriptionstring
    enobject
    namestring
    descriptionstring
    ptobject
    namestring
    descriptionstring
    frobject
    namestring
    descriptionstring
    deobject
    namestring
    descriptionstring
    itobject
    namestring
    descriptionstring

    Erros

    • 400 VALIDATION — a field is missing or malformed; `issues` names it
    • 401 UNAUTHORIZED — the key is missing, unknown or revoked
    • 402 NOT_PREMIUM — the restaurant has no active subscription or trial
    • 403 FORBIDDEN — the key lacks the scope this endpoint needs
    • 404 NOT_FOUND — no such restaurant in this account, or no such item in it
    • 500 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

    NomeOndeDescrição
    ref*caminhoThe restaurant identifier (the slug in its public URL, e.g. "pizzeria-roma") or its id, as returned by GET /restaurants

    Corpo (JSON)

    CampoTipoDescrição
    updates*object[]Up to 50 products, each with only the fields to change
    product_id*stringaté 128 caracteres
    changes*object
    category_idstringMove the product to another categoryaté 128 caracteres
    namestringProduct nameaté 50 caracteres
    pricenumberPrice in the restaurant's currency, in major units (12500 for COP $12.000, 5.99 for USD $5.99). Never cents.min 0
    descriptionstringOptional description. Pass an empty string to remove it.até 3000 caracteres
    original_pricenumber | nullThe price before a promotion. Must be greater than price; the menu shows it struck through. Pass null to end the promotion.min 0
    availablebooleanfalse shows the product as "Sin stock": visible but not orderable. Default true.
    hiddenbooleantrue keeps the product off the public menu entirely. Default false.
    variantsobject[]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*stringVariant name, e.g. "Tamaño" or "Adiciones"até 50 caracteres
    translationsobjectThe 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_selectionsintegerOptions the diner must pick. 0 makes the variant optional.min 0 · max 9007199254740991 · padrão 0
    max_selectionsintegerOptions the diner may pick. 0 is no limit; 1 renders a single choice.min 0 · max 9007199254740991 · padrão 0
    max_per_optionintegerHow many times one option may be repeated. Omit or 0 for no limit.min 0 · max 9007199254740991
    options*object[]
    translationsobjectThe 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.
    esobject
    enobject
    ptobject
    frobject
    deobject
    itobject

    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

    CampoTipoDescrição
    updated*integermin -9007199254740991 · max 9007199254740991

    Erros

    • 400 VALIDATION — a field is missing or malformed; `issues` names it
    • 401 UNAUTHORIZED — the key is missing, unknown or revoked
    • 402 NOT_PREMIUM — the restaurant has no active subscription or trial
    • 403 FORBIDDEN — the key lacks the scope this endpoint needs
    • 404 NOT_FOUND — no such restaurant in this account, or no such item in it
    • 500 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

    NomeOndeDescrição
    ref*caminhoThe restaurant identifier (the slug in its public URL, e.g. "pizzeria-roma") or its id, as returned by GET /restaurants
    id*caminhoThe 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

    CampoTipoDescrição
    product*object
    id*string
    category_id*string
    name*string
    price*numberMajor units in the restaurant's currency
    original_pricenumberPresent while the product is on promotion
    descriptionstring
    available*booleanfalse renders "Sin stock"
    hidden*booleantrue keeps it off the public menu
    has_image*boolean
    image_urlstring
    variants*object[]
    id*string
    name*string
    min_selections*integermin -9007199254740991 · max 9007199254740991
    max_selections*integer0 is no limitmin -9007199254740991 · max 9007199254740991
    max_per_optionintegermin -9007199254740991 · max 9007199254740991
    options*object[]
    id*string
    name*string
    price*numberExtra charge on top of the product price
    show*boolean
    translationsobjectThe 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`.
    translationsobjectThe 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`.
    esobject
    enobject
    ptobject
    frobject
    deobject
    itobject
    translationsobjectName 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`.
    esobject
    namestring
    descriptionstring
    enobject
    namestring
    descriptionstring
    ptobject
    namestring
    descriptionstring
    frobject
    namestring
    descriptionstring
    deobject
    namestring
    descriptionstring
    itobject
    namestring
    descriptionstring

    Erros

    • 401 UNAUTHORIZED — the key is missing, unknown or revoked
    • 402 NOT_PREMIUM — the restaurant has no active subscription or trial
    • 403 FORBIDDEN — the key lacks the scope this endpoint needs
    • 404 NOT_FOUND — no such restaurant in this account, or no such item in it
    • 500 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

    NomeOndeDescrição
    ref*caminhoThe restaurant identifier (the slug in its public URL, e.g. "pizzeria-roma") or its id, as returned by GET /restaurants
    id*caminhoThe product id, from GET /restaurants/{ref}/products

    Corpo (JSON)

    CampoTipoDescrição
    category_idstringMove the product to another categoryaté 128 caracteres
    namestringProduct nameaté 50 caracteres
    pricenumberPrice in the restaurant's currency, in major units (12500 for COP $12.000, 5.99 for USD $5.99). Never cents.min 0
    descriptionstringOptional description. Pass an empty string to remove it.até 3000 caracteres
    original_pricenumber | nullThe price before a promotion. Must be greater than price; the menu shows it struck through. Pass null to end the promotion.min 0
    availablebooleanfalse shows the product as "Sin stock": visible but not orderable. Default true.
    hiddenbooleantrue keeps the product off the public menu entirely. Default false.
    variantsobject[]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*stringVariant name, e.g. "Tamaño" or "Adiciones"até 50 caracteres
    translationsobjectThe 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.
    esobject
    namestringThe name in that languageaté 50 caracteres
    enobject
    namestringThe name in that languageaté 50 caracteres
    ptobject
    namestringThe name in that languageaté 50 caracteres
    frobject
    namestringThe name in that languageaté 50 caracteres
    deobject
    namestringThe name in that languageaté 50 caracteres
    itobject
    namestringThe name in that languageaté 50 caracteres
    min_selectionsintegerOptions the diner must pick. 0 makes the variant optional.min 0 · max 9007199254740991 · padrão 0
    max_selectionsintegerOptions the diner may pick. 0 is no limit; 1 renders a single choice.min 0 · max 9007199254740991 · padrão 0
    max_per_optionintegerHow many times one option may be repeated. Omit or 0 for no limit.min 0 · max 9007199254740991
    options*object[]
    name*stringOption name, e.g. "Grande"até 50 caracteres
    translationsobjectThe 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.
    esobject
    enobject
    ptobject
    frobject
    deobject
    itobject
    pricenumberExtra charge for this option on top of the product price. 0 when it costs nothing.min 0 · padrão 0
    showbooleanfalse hides the option from diners without deleting itpadrão true
    translationsobjectThe 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.
    esobject
    namestringThe product name in that languageaté 50 caracteres
    descriptionstringThe description in that languageaté 3000 caracteres
    enobject
    namestringThe product name in that languageaté 50 caracteres
    descriptionstringThe description in that languageaté 3000 caracteres
    ptobject
    namestringThe product name in that languageaté 50 caracteres
    descriptionstringThe description in that languageaté 3000 caracteres
    frobject
    namestringThe product name in that languageaté 50 caracteres
    descriptionstringThe description in that languageaté 3000 caracteres
    deobject
    namestringThe product name in that languageaté 50 caracteres
    descriptionstringThe description in that languageaté 3000 caracteres
    itobject
    namestringThe product name in that languageaté 50 caracteres
    descriptionstringThe 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

    CampoTipoDescrição
    product*object
    id*string
    category_id*string
    name*string
    price*numberMajor units in the restaurant's currency
    original_pricenumberPresent while the product is on promotion
    descriptionstring
    available*booleanfalse renders "Sin stock"
    hidden*booleantrue keeps it off the public menu
    has_image*boolean
    image_urlstring
    variants*object[]
    id*string
    name*string
    min_selections*integermin -9007199254740991 · max 9007199254740991
    max_selections*integer0 is no limitmin -9007199254740991 · max 9007199254740991
    max_per_optionintegermin -9007199254740991 · max 9007199254740991
    options*object[]
    id*string
    name*string
    price*numberExtra charge on top of the product price
    show*boolean
    translationsobjectThe 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`.
    translationsobjectThe 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`.
    esobject
    enobject
    ptobject
    frobject
    deobject
    itobject
    translationsobjectName 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`.
    esobject
    namestring
    descriptionstring
    enobject
    namestring
    descriptionstring
    ptobject
    namestring
    descriptionstring
    frobject
    namestring
    descriptionstring
    deobject
    namestring
    descriptionstring
    itobject
    namestring
    descriptionstring

    Erros

    • 400 VALIDATION — a field is missing or malformed; `issues` names it
    • 401 UNAUTHORIZED — the key is missing, unknown or revoked
    • 402 NOT_PREMIUM — the restaurant has no active subscription or trial
    • 403 FORBIDDEN — the key lacks the scope this endpoint needs
    • 404 NOT_FOUND — no such restaurant in this account, or no such item in it
    • 500 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

    NomeOndeDescrição
    ref*caminhoThe restaurant identifier (the slug in its public URL, e.g. "pizzeria-roma") or its id, as returned by GET /restaurants
    id*caminhoThe 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

    CampoTipoDescrição
    deleted*string

    Erros

    • 401 UNAUTHORIZED — the key is missing, unknown or revoked
    • 402 NOT_PREMIUM — the restaurant has no active subscription or trial
    • 403 FORBIDDEN — the key lacks the scope this endpoint needs
    • 404 NOT_FOUND — no such restaurant in this account, or no such item in it
    • 500 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

    NomeOndeDescrição
    ref*caminhoThe 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)

    CampoTipoDescrição
    image_url*stringPublic 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

    CampoTipoDescrição
    restaurant*object
    id*string
    identifier*string
    name*string
    currency*stringISO 4217 code every price is in
    type*"whatsapp" | "read_only""whatsapp" takes orders by WhatsApp; "read_only" only shows the menu
    phone*string | nullWhatsApp 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 it
    • 401 UNAUTHORIZED — the key is missing, unknown or revoked
    • 402 NOT_PREMIUM — the restaurant has no active subscription or trial
    • 403 FORBIDDEN — the key lacks the scope this endpoint needs
    • 404 NOT_FOUND — no such restaurant in this account, or no such item in it
    • 500 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

    NomeOndeDescrição
    ref*caminhoThe 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

    CampoTipoDescrição
    restaurant*object
    id*string
    identifier*string
    name*string
    currency*stringISO 4217 code every price is in
    type*"whatsapp" | "read_only""whatsapp" takes orders by WhatsApp; "read_only" only shows the menu
    phone*string | nullWhatsApp 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 revoked
    • 402 NOT_PREMIUM — the restaurant has no active subscription or trial
    • 403 FORBIDDEN — the key lacks the scope this endpoint needs
    • 404 NOT_FOUND — no such restaurant in this account, or no such item in it
    • 500 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

    NomeOndeDescrição
    ref*caminhoThe restaurant identifier (the slug in its public URL, e.g. "pizzeria-roma") or its id, as returned by GET /restaurants
    id*caminhoThe product id, from GET /restaurants/{ref}/products

    Corpo (JSON)

    CampoTipoDescrição
    image_url*stringPublic 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

    CampoTipoDescrição
    product*object
    id*string
    category_id*string
    name*string
    price*numberMajor units in the restaurant's currency
    original_pricenumberPresent while the product is on promotion
    descriptionstring
    available*booleanfalse renders "Sin stock"
    hidden*booleantrue keeps it off the public menu
    has_image*boolean
    image_urlstring
    variants*object[]
    id*string
    name*string
    min_selections*integermin -9007199254740991 · max 9007199254740991
    max_selections*integer0 is no limitmin -9007199254740991 · max 9007199254740991
    max_per_optionintegermin -9007199254740991 · max 9007199254740991
    options*object[]
    id*string
    name*string
    price*numberExtra charge on top of the product price
    show*boolean
    translationsobjectThe 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`.
    translationsobjectThe 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`.
    esobject
    enobject
    ptobject
    frobject
    deobject
    itobject
    translationsobjectName 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`.
    esobject
    namestring
    descriptionstring
    enobject
    namestring
    descriptionstring
    ptobject
    namestring
    descriptionstring
    frobject
    namestring
    descriptionstring
    deobject
    namestring
    descriptionstring
    itobject
    namestring
    descriptionstring

    Erros

    • 400 VALIDATION — a field is missing or malformed; `issues` names it
    • 401 UNAUTHORIZED — the key is missing, unknown or revoked
    • 402 NOT_PREMIUM — the restaurant has no active subscription or trial
    • 403 FORBIDDEN — the key lacks the scope this endpoint needs
    • 404 NOT_FOUND — no such restaurant in this account, or no such item in it
    • 500 INTERNAL — something failed on our side; retry in a moment
    delete/restaurants/{ref}/products/{id}/imagerequer menu:writeremoveProductImage

    Remove the product photo

    Parâmetros

    NomeOndeDescrição
    ref*caminhoThe restaurant identifier (the slug in its public URL, e.g. "pizzeria-roma") or its id, as returned by GET /restaurants
    id*caminhoThe 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

    CampoTipoDescrição
    product_id*string
    has_image*false

    Erros

    • 401 UNAUTHORIZED — the key is missing, unknown or revoked
    • 402 NOT_PREMIUM — the restaurant has no active subscription or trial
    • 403 FORBIDDEN — the key lacks the scope this endpoint needs
    • 404 NOT_FOUND — no such restaurant in this account, or no such item in it
    • 500 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

    NomeOndeDescrição
    ref*caminhoThe 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

    CampoTipoDescriçã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 | nullHTTP status of the last attempt; null if it never answeredmin -9007199254740991 · max 9007199254740991

    Erros

    • 401 UNAUTHORIZED — the key is missing, unknown or revoked
    • 402 NOT_PREMIUM — the restaurant has no active subscription or trial
    • 403 FORBIDDEN — the key lacks the scope this endpoint needs
    • 404 NOT_FOUND — no such restaurant in this account, or no such item in it
    • 500 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

    NomeOndeDescrição
    ref*caminhoThe restaurant identifier (the slug in its public URL, e.g. "pizzeria-roma") or its id, as returned by GET /restaurants

    Corpo (JSON)

    CampoTipoDescrição
    url*stringWhere 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

    CampoTipoDescriçã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 | nullHTTP status of the last attempt; null if it never answeredmin -9007199254740991 · max 9007199254740991
    secret*stringThe signing secret. Shown only in this response; store it now

    Erros

    • 400 VALIDATION — a field is missing or malformed; `issues` names it
    • 401 UNAUTHORIZED — the key is missing, unknown or revoked
    • 402 NOT_PREMIUM — the restaurant has no active subscription or trial
    • 403 FORBIDDEN — the key lacks the scope this endpoint needs
    • 404 NOT_FOUND — no such restaurant in this account, or no such item in it
    • 409 CONFLICT — this restaurant already has a webhook with that URL
    • 500 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

    NomeOndeDescrição
    ref*caminhoThe restaurant identifier (the slug in its public URL, e.g. "pizzeria-roma") or its id, as returned by GET /restaurants
    id*caminhoThe webhook id, from GET /restaurants/{ref}/webhooks

    Corpo (JSON)

    CampoTipoDescrição
    urlstringWhere 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
    enabledbooleanfalse 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

    CampoTipoDescriçã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 | nullHTTP status of the last attempt; null if it never answeredmin -9007199254740991 · max 9007199254740991

    Erros

    • 400 VALIDATION — a field is missing or malformed; `issues` names it
    • 401 UNAUTHORIZED — the key is missing, unknown or revoked
    • 402 NOT_PREMIUM — the restaurant has no active subscription or trial
    • 403 FORBIDDEN — the key lacks the scope this endpoint needs
    • 404 NOT_FOUND — no such restaurant in this account, or no such item in it
    • 409 CONFLICT — this restaurant already has a webhook with that URL
    • 500 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

    NomeOndeDescrição
    ref*caminhoThe restaurant identifier (the slug in its public URL, e.g. "pizzeria-roma") or its id, as returned by GET /restaurants
    id*caminhoThe 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

    CampoTipoDescrição
    deleted*string

    Erros

    • 401 UNAUTHORIZED — the key is missing, unknown or revoked
    • 402 NOT_PREMIUM — the restaurant has no active subscription or trial
    • 403 FORBIDDEN — the key lacks the scope this endpoint needs
    • 404 NOT_FOUND — no such restaurant in this account, or no such item in it
    • 500 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

    NomeOndeDescrição
    ref*caminhoThe restaurant identifier (the slug in its public URL, e.g. "pizzeria-roma") or its id, as returned by GET /restaurants
    id*caminhoThe 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

    CampoTipoDescriçã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 | nullHTTP status of the last attempt; null if it never answeredmin -9007199254740991 · max 9007199254740991
    secret*stringThe signing secret. Shown only in this response; store it now

    Erros

    • 401 UNAUTHORIZED — the key is missing, unknown or revoked
    • 402 NOT_PREMIUM — the restaurant has no active subscription or trial
    • 403 FORBIDDEN — the key lacks the scope this endpoint needs
    • 404 NOT_FOUND — no such restaurant in this account, or no such item in it
    • 500 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

    NomeOndeDescrição
    ref*caminhoThe restaurant identifier (the slug in its public URL, e.g. "pizzeria-roma") or its id, as returned by GET /restaurants
    id*caminhoThe 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

    CampoTipoDescrição
    delivery*object
    id*string
    event_id*string
    type*string
    attempt*integer1 for the first try, then each retrymin -9007199254740991 · max 9007199254740991
    ok*boolean
    http_status*integer | nullmin -9007199254740991 · max 9007199254740991
    duration_ms*integermin -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 revoked
    • 402 NOT_PREMIUM — the restaurant has no active subscription or trial
    • 403 FORBIDDEN — the key lacks the scope this endpoint needs
    • 404 NOT_FOUND — no such restaurant in this account, or no such item in it
    • 500 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

    NomeOndeDescrição
    ref*caminhoThe restaurant identifier (the slug in its public URL, e.g. "pizzeria-roma") or its id, as returned by GET /restaurants
    id*caminhoThe 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

    CampoTipoDescrição
    deliveries*object[]
    id*string
    event_id*string
    type*string
    attempt*integer1 for the first try, then each retrymin -9007199254740991 · max 9007199254740991
    ok*boolean
    http_status*integer | nullmin -9007199254740991 · max 9007199254740991
    duration_ms*integermin -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 revoked
    • 402 NOT_PREMIUM — the restaurant has no active subscription or trial
    • 403 FORBIDDEN — the key lacks the scope this endpoint needs
    • 404 NOT_FOUND — no such restaurant in this account, or no such item in it
    • 500 INTERNAL — something failed on our side; retry in a moment

    Desenvolvido por weeeb 🧡

    Delimenu

    • Como funciona?
    • Preços
    • Perguntas frequentes
    • Status da plataforma
    • Central de ajuda
    • Desenvolvedores
    • Ideias e feedback
    • Roadmap
    • Registro de alterações

    Legal

    • Termos e condições
    • Privacidade
    • Cookies

    Siga-nos

    Siga-nos nas redes sociais e fique por dentro de todas as novidades.

    Copyright © 2026 Weeeb LLC. All rights reserved.

    EspañolEnglishPortuguês