Ir al contenido principal

Endpoints de webhooks

Registrar, probar y administrar los endpoints que reciben los eventos del menú.

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.

Cada operación trae su curl y su respuesta de ejemplo; las claves y los ids de los ejemplos son ficticios. Los campos marcados con * son obligatorios.

List the webhooks of a restaurant

get/restaurants/{ref}/webhooksrequiere menu:readlistWebhooks

Signing secrets are never returned here; only when an endpoint is created or its secret rotated.

Parámetros

NombreDóndeDescripción
ref*rutaThe restaurant identifier (the slug in its public URL, e.g. "pizzeria-roma") or its id, as returned by GET /restaurants

Ejemplo

curl -X GET "https://delimenu.co/api/v1/restaurants/pizzeria-roma/webhooks" \
  -H "Authorization: Bearer dmk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

Respuesta 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 de la respuesta

CampoTipoDescripción
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

Errores

  • 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

Register a webhook

post/restaurants/{ref}/webhooksrequiere menu:writecreateWebhook

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

NombreDóndeDescripción
ref*rutaThe restaurant identifier (the slug in its public URL, e.g. "pizzeria-roma") or its id, as returned by GET /restaurants

Cuerpo (JSON)

CampoTipoDescripción
url*stringWhere the events are POSTed: https, a public domain, port 443hasta 2048 caracteres
events*"product.created" | "product.updated" | "product.deleted" | "category.created" | "category.updated" | "category.deleted" | "restaurant.updated" | "restaurant.deleted"[]The events this endpoint receives

Ejemplo

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"]}'

Respuesta 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 de la respuesta

CampoTipoDescripción
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

Errores

  • 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

Update a webhook

patch/restaurants/{ref}/webhooks/{id}requiere menu:writeupdateWebhook

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

NombreDóndeDescripción
ref*rutaThe restaurant identifier (the slug in its public URL, e.g. "pizzeria-roma") or its id, as returned by GET /restaurants
id*rutaThe webhook id, from GET /restaurants/{ref}/webhooks

Cuerpo (JSON)

CampoTipoDescripción
urlstringWhere the events are POSTed: https, a public domain, port 443hasta 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

Ejemplo

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}'

Respuesta 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 de la respuesta

CampoTipoDescripción
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

Errores

  • 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 a webhook

delete/restaurants/{ref}/webhooks/{id}requiere menu:writedeleteWebhook

Stops every delivery at once, retries already queued included.

Parámetros

NombreDóndeDescripción
ref*rutaThe restaurant identifier (the slug in its public URL, e.g. "pizzeria-roma") or its id, as returned by GET /restaurants
id*rutaThe webhook id, from GET /restaurants/{ref}/webhooks

Ejemplo

curl -X DELETE "https://delimenu.co/api/v1/restaurants/pizzeria-roma/webhooks/w7Kd2PqR9sLm4XvB1nTc" \
  -H "Authorization: Bearer dmk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

Respuesta 200

{
  "deleted": "w7Kd2PqR9sLm4XvB1nTc"
}

Campos de la respuesta

CampoTipoDescripción
deleted*string

Errores

  • 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

Rotate the signing secret

post/restaurants/{ref}/webhooks/{id}/secretrequiere menu:writerotateWebhookSecret

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

NombreDóndeDescripción
ref*rutaThe restaurant identifier (the slug in its public URL, e.g. "pizzeria-roma") or its id, as returned by GET /restaurants
id*rutaThe webhook id, from GET /restaurants/{ref}/webhooks

Ejemplo

curl -X POST "https://delimenu.co/api/v1/restaurants/pizzeria-roma/webhooks/w7Kd2PqR9sLm4XvB1nTc/secret" \
  -H "Authorization: Bearer dmk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

Respuesta 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 de la respuesta

CampoTipoDescripción
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

Errores

  • 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

Send a test event

post/restaurants/{ref}/webhooks/{id}/testrequiere menu:writetestWebhook

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

NombreDóndeDescripción
ref*rutaThe restaurant identifier (the slug in its public URL, e.g. "pizzeria-roma") or its id, as returned by GET /restaurants
id*rutaThe webhook id, from GET /restaurants/{ref}/webhooks

Ejemplo

curl -X POST "https://delimenu.co/api/v1/restaurants/pizzeria-roma/webhooks/w7Kd2PqR9sLm4XvB1nTc/test" \
  -H "Authorization: Bearer dmk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

Respuesta 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 de la respuesta

CampoTipoDescripción
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

Errores

  • 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

Latest delivery attempts

get/restaurants/{ref}/webhooks/{id}/deliveriesrequiere menu:readlistWebhookDeliveries

The most recent attempts to this endpoint, newest first, retries included. Kept for 14 days.

Parámetros

NombreDóndeDescripción
ref*rutaThe restaurant identifier (the slug in its public URL, e.g. "pizzeria-roma") or its id, as returned by GET /restaurants
id*rutaThe webhook id, from GET /restaurants/{ref}/webhooks

Ejemplo

curl -X GET "https://delimenu.co/api/v1/restaurants/pizzeria-roma/webhooks/w7Kd2PqR9sLm4XvB1nTc/deliveries" \
  -H "Authorization: Bearer dmk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

Respuesta 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 de la respuesta

CampoTipoDescripción
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

Errores

  • 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

Desarrolladores