> ## Documentation Index
> Fetch the complete documentation index at: https://storekit.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Ordermeldingen naar Slack of je telefoon

> Slack- of sms-alert wanneer een grote bestelling binnenkomt, een bezorging wordt geplaatst of een POS-verzending mislukt, met storekit-webhooks en regels.

Een kleine app die je bestellingen in de gaten houdt en de juiste persoon vertelt over de bestellingen die er echt toe doen: grote bestellingen, bezorgingen en bestellingen die de kassa niet hebben bereikt. Regels kun je zelf bewerken vanaf een instellingenpagina — geen redeploy nodig om een drempel te wijzigen.

> [!TIP]
>
> Als je alleen elke bestelling in een Slack-kanaal wilt posten zonder regels, hoef je niets te bouwen: de webhooks-portal van storekit kan rechtstreeks naar Slack posten. Zie [Slack-meldingen](/docs/nl/developers/webhooks/slack-integration). Bouw deze recipe wanneer je filtering, tekstberichten of verschillende personen voor verschillende events nodig hebt.

## Hoe het werkt

1. storekit stuurt `order.created.v2` en `order.pos.dispatch.failed` naar je app.
2. Je app controleert elk event tegen de regels die je hebt ingesteld (orderwaarde, ordertype, trefwoorden in notities) en kiest een kanaal.
3. Het post naar Slack via een incoming webhook, en optioneel stuurt het een tekstbericht via Twilio.

## Wat je nodig hebt

* Webhooks ingeschakeld op je storekit-abonnement (**Settings** → **Developers**).
* Een [Lovable](https://lovable.dev)- of [v0](https://v0.dev)-account. Beide hebben een backend nodig voor het webhook-endpoint — de prompt vraagt de builder er een te deployen en je de URL te geven. [Replit](https://replit.com) werkt ook en is de eenvoudigere keuze als de builder moeite heeft met de backend-stap.
* Een Slack-incoming-webhook-URL voor het kanaal waarin je meldingen wilt (maak er een aan vanuit de "Incoming Webhooks"-app in Slack's app-directory, kies het kanaal en kopieer de URL).
* Optioneel, voor tekstberichten: een [Twilio](https://twilio.com)-account met een telefoonnummer. Tekstberichten kosten geld per bericht; Slack is gratis.

## Stap 1: Bouw de app

Plak het hele blok in de builder in één bericht. Wijzig eerst de voorbeeldregels onder **MY RULES** om bij je bedrijf te passen.

[![](https://mintcdn.com/storekit/IGeB_84apyho3Z6U/images/logos/lovable.svg?fit=max\&auto=format\&n=IGeB_84apyho3Z6U\&q=85\&s=cc21d11769d5e6b686fed56390a88f5e) Build with Lovable](https://lovable.dev/#prompt=I%20want%20a%20small%20alerting%20app%20for%20my%20restaurant.%20storekit%20sends%20it%20a%20webhook%20whenever%20an%20order%20is%20placed%20or%20an%20order%20fails%20to%20reach%20my%20till%2C%20and%20it%20decides%20whether%20anyone%20needs%20to%20know%20and%20tells%20them%20on%20Slack%20or%20by%20text.%20It%20must%20never%20miss%20an%20alert%2C%20never%20send%20the%20same%20alert%20twice%2C%20and%20never%20make%20storekit%20wait%20for%20a%20reply.%0A%0ABuild%20it%20with%20a%20backend%20that%20has%20a%20public%20HTTPS%20URL%20for%20the%20webhook%20\(deploy%20it%20and%20tell%20me%20the%20URL\)%2C%20a%20small%20database%20for%20rules%20and%20history%2C%20and%20a%20settings%20page%20I%20can%20use%20from%20my%20phone.%0A%0ACONTEXT%20ABOUT%20STOREKIT%0A%0AI%20run%20a%20hospitality%20business%20on%20storekit%20\(storekit.com\)%2C%20an%20online%20ordering%20and%20payments%20platform%20for%20restaurants%2C%20caf%C3%A9s%20and%20bars.%20storekit%20can%20notify%20my%20own%20app%20about%20things%20that%20happen%20in%20my%20store%20by%20sending%20webhooks.%0A%0AHow%20storekit%20webhooks%20work%3A%0A-%20Each%20event%20is%20an%20HTTP%20POST%20with%20a%20JSON%20body%20to%20a%20URL%20I%20register%20in%20the%20storekit%20dashboard.%0A-%20The%20body%20has%20the%20shape%20%7B%20%22event%22%3A%20%22%3Cevent%20type%3E%22%2C%20%22data%22%3A%20%7B%20...%20%7D%20%7D.%20Two%20exceptions%3A%20the%20legacy%20order.created%20event%20\(do%20not%20use%20it%3B%20use%20order.created.v2\)%20sends%20the%20bare%20order%20object%2C%20and%20the%20order.refund.created%20%2F%20order.refund.succeeded%20%2F%20order.refund.failed%20events%20put%20order%2C%20customer%2C%20venue%20and%20refund%20next%20to%20event%20instead%20of%20inside%20data.%0A-%20Requests%20are%20signed%20with%20Svix.%20Every%20request%20carries%20the%20headers%20svix-id%2C%20svix-timestamp%20and%20svix-signature.%20Verify%20them%20with%20the%20official%20%60svix%60%20library%20for%20your%20language%2C%20using%20a%20secret%20I%20will%20provide%20in%20an%20environment%20variable%20called%20STOREKIT_WEBHOOK_SECRET.%20Reject%20anything%20that%20fails%20verification%20with%20HTTP%20400.%0A-%20My%20endpoint%20must%20respond%20with%20a%202xx%20status%20within%2015%20seconds.%20Save%20the%20event%20to%20the%20database%20first%2C%20respond%20200%2C%20then%20do%20the%20work%20from%20the%20saved%20copy.%0A-%20Once%20I%20have%20responded%20200%2C%20storekit%20will%20not%20send%20that%20event%20again%2C%20so%20the%20work%20must%20not%20be%20lost%3A%20store%20each%20event%20with%20a%20status%20\(pending%2C%20done%2C%20failed\)%20and%20only%20mark%20it%20done%20after%20the%20work%20has%20actually%20succeeded.%20On%20startup%20and%20on%20a%20timer%2C%20pick%20up%20anything%20still%20pending%20or%20failed%20and%20retry%20it.%20Never%20do%20the%20work%20only%20in%20memory%20after%20responding.%0A-%20The%20same%20event%20can%20occasionally%20be%20delivered%20more%20than%20once.%20Use%20the%20svix-id%20header%20as%20the%20unique%20key%20for%20the%20saved%20event%3A%20a%20repeat%20of%20an%20event%20already%20saved%20is%20ignored%2C%20whatever%20its%20status.%0A-%20Any%20page%20or%20endpoint%20protected%20by%20a%20password%20must%20be%20served%20over%20HTTPS%20only%3B%20redirect%20or%20refuse%20plain%20HTTP.%0A-%20Money%20fields%20\(total%2C%20tip%2C%20deliveryFee%2C%20discountTotal%2C%20item%20price%2C%20modifier%20price\)%20are%20integers%20in%20minor%20units%3A%202500%20means%20%C2%A325.00.%0A-%20orderType%20is%20one%20of%20Delivery%2C%20Pickup%2C%20InStore%2C%20Curbside%2C%20CateringDelivery%2C%20CateringPickup%20or%20Billpay.%0A-%20table%20is%20only%20present%20for%20in-store%20orders%3B%20deliveryAddress%20and%20pickUpTime%20only%20for%20delivery%20orders.%20When%20they%20do%20not%20apply%20the%20key%20is%20omitted%2C%20so%20treat%20a%20missing%20key%20the%20same%20as%20null.%20notes%20and%20items%5B%5D.note%20can%20be%20null%20or%20empty.%20customer.companyName%20and%20customer.vatNumber%20appear%20only%20when%20supplied.%0A%0AExample%20order.created.v2%20payload%3A%0A%0A%7B%0A%20%20%22event%22%3A%20%22order.created.v2%22%2C%0A%20%20%22data%22%3A%20%7B%0A%20%20%20%20%22id%22%3A%20%22ord_abc123%22%2C%0A%20%20%20%20%22code%22%3A%20%22A1B2%22%2C%0A%20%20%20%20%22asap%22%3A%20true%2C%0A%20%20%20%20%22total%22%3A%202500%2C%0A%20%20%20%20%22tip%22%3A%20250%2C%0A%20%20%20%20%22deliveryFee%22%3A%20299%2C%0A%20%20%20%20%22discountTotal%22%3A%200%2C%0A%20%20%20%20%22orderType%22%3A%20%22Delivery%22%2C%0A%20%20%20%20%22pickUpTime%22%3A%20%222024-01-15T10%3A45%3A00Z%22%2C%0A%20%20%20%20%22channel%22%3A%20%22WEB%22%2C%0A%20%20%20%20%22poNumber%22%3A%20null%2C%0A%20%20%20%20%22createdAt%22%3A%20%222024-01-15T10%3A30%3A00Z%22%2C%0A%20%20%20%20%22deliveryTime%22%3A%20%222024-01-15T11%3A00%3A00Z%22%2C%0A%20%20%20%20%22notes%22%3A%20%22Ring%20doorbell%22%2C%0A%20%20%20%20%22customer%22%3A%20%7B%0A%20%20%20%20%20%20%22firstName%22%3A%20%22John%22%2C%0A%20%20%20%20%20%20%22lastName%22%3A%20%22Doe%22%2C%0A%20%20%20%20%20%20%22email%22%3A%20%22john%40example.com%22%2C%0A%20%20%20%20%20%20%22phone%22%3A%20%22%2B44123456789%22%2C%0A%20%20%20%20%20%20%22marketingConsent%22%3A%20true%0A%20%20%20%20%7D%2C%0A%20%20%20%20%22items%22%3A%20%5B%0A%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%22name%22%3A%20%22Margherita%20Pizza%22%2C%0A%20%20%20%20%20%20%20%20%22printName%22%3A%20%22Margherita%22%2C%0A%20%20%20%20%20%20%20%20%22price%22%3A%201200%2C%0A%20%20%20%20%20%20%20%20%22quantity%22%3A%201%2C%0A%20%20%20%20%20%20%20%20%22plu%22%3A%20null%2C%0A%20%20%20%20%20%20%20%20%22posId%22%3A%20null%2C%0A%20%20%20%20%20%20%20%20%22taxRate%22%3A%2020%2C%0A%20%20%20%20%20%20%20%20%22note%22%3A%20null%2C%0A%20%20%20%20%20%20%20%20%22modifiers%22%3A%20%5B%0A%20%20%20%20%20%20%20%20%20%20%7B%20%22name%22%3A%20%22Extra%20cheese%22%2C%20%22printName%22%3A%20%22Cheese%22%2C%20%22price%22%3A%20150%2C%20%22quantity%22%3A%201%2C%20%22plu%22%3A%20null%2C%20%22posId%22%3A%20null%2C%20%22taxRate%22%3A%2020%20%7D%0A%20%20%20%20%20%20%20%20%5D%0A%20%20%20%20%20%20%7D%0A%20%20%20%20%5D%2C%0A%20%20%20%20%22venue%22%3A%20%7B%0A%20%20%20%20%20%20%22id%22%3A%201234%2C%0A%20%20%20%20%20%20%22name%22%3A%20%22My%20Restaurant%22%2C%0A%20%20%20%20%20%20%22slug%22%3A%20%22my-restaurant%22%2C%0A%20%20%20%20%20%20%22address%22%3A%20%7B%0A%20%20%20%20%20%20%20%20%22street1%22%3A%20%22123%20Main%20St%22%2C%0A%20%20%20%20%20%20%20%20%22street2%22%3A%20%22%22%2C%0A%20%20%20%20%20%20%20%20%22city%22%3A%20%22London%22%2C%0A%20%20%20%20%20%20%20%20%22postCode%22%3A%20%22W1A%201AA%22%2C%0A%20%20%20%20%20%20%20%20%22country%22%3A%20%22UK%22%2C%0A%20%20%20%20%20%20%20%20%22companyName%22%3A%20%22My%20Restaurant%20Ltd%22%2C%0A%20%20%20%20%20%20%20%20%22coordinates%22%3A%20%7B%20%22latitude%22%3A%2051.5074%2C%20%22longitude%22%3A%20-0.1278%20%7D%0A%20%20%20%20%20%20%7D%0A%20%20%20%20%7D%2C%0A%20%20%20%20%22deliveryAddress%22%3A%20%7B%0A%20%20%20%20%20%20%22street1%22%3A%20%22456%20Oak%20Ave%22%2C%0A%20%20%20%20%20%20%22street2%22%3A%20%22Flat%202%22%2C%0A%20%20%20%20%20%20%22city%22%3A%20%22London%22%2C%0A%20%20%20%20%20%20%22postCode%22%3A%20%22E1%206AN%22%2C%0A%20%20%20%20%20%20%22country%22%3A%20%22UK%22%2C%0A%20%20%20%20%20%20%22notes%22%3A%20null%2C%0A%20%20%20%20%20%20%22coordinates%22%3A%20%7B%20%22latitude%22%3A%2051.5155%2C%20%22longitude%22%3A%20-0.0722%20%7D%0A%20%20%20%20%7D%0A%20%20%7D%0A%7D%0A%0AThe%20full%20list%20of%20event%20types%20and%20payloads%20is%20at%20https%3A%2F%2Fstorekit.com%2Fdocs%2Fdevelopers%2Fwebhooks%2Fwebhook-events%0A%0ATHE%20SECOND%20EVENT%3A%20order.pos.dispatch.failed%0A%0Astorekit%20also%20sends%20this%20event%20when%20an%20order%20could%20not%20be%20pushed%20to%20my%20point-of-sale%20system.%20It%20has%20a%20different%2C%20smaller%20payload%3A%0A%0A%7B%0A%20%20%22event%22%3A%20%22order.pos.dispatch.failed%22%2C%0A%20%20%22data%22%3A%20%7B%0A%20%20%20%20%22order%22%3A%20%7B%20%22id%22%3A%20%22ord_abc123%22%2C%20%22code%22%3A%20%22A1B2%22%2C%20%22total%22%3A%202500%20%7D%2C%0A%20%20%20%20%22venue%22%3A%20%7B%20%22id%22%3A%201234%2C%20%22name%22%3A%20%22My%20Restaurant%22%2C%20%22slug%22%3A%20%22my-restaurant%22%20%7D%2C%0A%20%20%20%20%22pos%22%3A%20%7B%20%22name%22%3A%20%22Zonal%22%2C%20%22error%22%3A%20%22Connection%20timeout%3A%20Unable%20to%20reach%20POS%20endpoint%22%20%7D%2C%0A%20%20%20%20%22retryCount%22%3A%203%0A%20%20%7D%0A%7D%0A%0AThis%20one%20is%20always%20urgent%3A%20it%20means%20the%20kitchen%20may%20not%20know%20about%20a%20paid%20order.%0A%0AWHAT%20TO%20BUILD%0A%0A1.%20POST%20%2Fwebhooks%2Fstorekit%20%E2%80%94%20verify%20the%20Svix%20signature%20with%20the%20official%20svix%20library%20and%20STOREKIT_WEBHOOK_SECRET%20against%20the%20raw%20request%20body%3B%20400%20on%20failure.%20Save%20the%20event%20and%20return%20200%20immediately%2C%20then%20evaluate%20rules%20and%20send%20messages%20in%20the%20background.%20Ignore%20any%20svix-id%20already%20saved.%20Handle%20order.created.v2%20and%20order.pos.dispatch.failed%3B%20save%20and%20acknowledge%20any%20other%20event%20type%20but%20do%20nothing%20with%20it.%0A2.%20A%20rules%20engine.%20Each%20rule%20has%3A%20a%20name%2C%20which%20event%20it%20applies%20to%2C%20conditions%2C%20one%20or%20more%20channels%20\(Slack%2C%20SMS%2C%20or%20both\)%2C%20a%20message%20template%2C%20and%20an%20on%2Foff%20switch.%20Conditions%20for%20order.created.v2%3A%20minimum%20total%20\(in%20pounds%2C%20convert%20to%20minor%20units%20when%20comparing\)%2C%20order%20type%20is%20one%20of%20a%20list%2C%20notes%20contain%20any%20of%20a%20list%20of%20words%20\(case-insensitive%2C%20also%20check%20each%20item's%20note%20field\)%2C%20and%20a%20time%20window%20\(only%20alert%20between%20HH%3AMM%20and%20HH%3AMM%20in%20TIMEZONE\).%20order.pos.dispatch.failed%20has%20no%20conditions%3A%20it%20always%20alerts%20on%20all%20of%20its%20rule's%20channels.%20Every%20matching%20rule%20fires%20on%20every%20channel%20it%20has%3B%20a%20rule%20fires%20at%20most%20once%20per%20order%20per%20channel%20\(order%20id%20%2B%20rule%20id%20%2B%20channel%20is%20unique\).%0A3.%20Slack%20delivery%20through%20an%20incoming%20webhook%20URL%20stored%20per%20rule%20\(SLACK_WEBHOOK_URL%20as%20the%20default\).%20Format%20the%20message%20with%20Slack's%20mrkdwn%3A%20bold%20first%20line%2C%20then%20one%20line%20per%20detail.%0A4.%20SMS%20delivery%20through%20Twilio%20using%20TWILIO_ACCOUNT_SID%2C%20TWILIO_AUTH_TOKEN%20and%20TWILIO_FROM_NUMBER%2C%20to%20one%20or%20more%20numbers%20stored%20on%20the%20rule.%20If%20the%20Twilio%20variables%20are%20unset%2C%20the%20SMS%20channel%20is%20shown%20as%20disabled%20in%20the%20settings%20page%20and%20is%20skipped%2C%20not%20errored%3B%20the%20rule's%20other%20channels%20still%20fire.%20Keep%20texts%20under%20160%20characters.%0A5.%20GET%20%2Fsettings%20%E2%80%94%20protected%20by%20HTTP%20basic%20auth%20with%20ADMIN_PASSWORD.%20Lists%20rules%20with%20on%2Foff%20toggles%2C%20lets%20me%20add%2C%20edit%20and%20delete%20rules%2C%20and%20has%20a%20%22Send%20test%22%20button%20on%20each%20rule%20that%20fires%20it%20with%20fake%20data.%20Below%20the%20rules%2C%20show%20the%20last%20100%20alerts%20sent%3A%20time%2C%20order%20code%2C%20rule%2C%20channel%2C%20and%20whether%20delivery%20succeeded%20\(store%20the%20Slack%20or%20Twilio%20response\).%0A6.%20If%20Slack%20or%20Twilio%20return%20an%20error%2C%20retry%20three%20times%20with%20a%20pause%20between%20attempts%2C%20then%20mark%20the%20alert%20failed%20in%20the%20history.%20Never%20retry%20a%20successful%20send.%0A7.%20GET%20%2Fhealth%20that%20returns%20200%20and%20%7B%20%22ok%22%3A%20true%20%7D%20and%20nothing%20else.%0A%0AMESSAGE%20TEMPLATES%0A%0ATemplates%20use%20placeholders%20in%20curly%20braces%3A%20%7Bcode%7D%2C%20%7Btotal%7D%2C%20%7BorderType%7D%2C%20%7BcustomerFirstName%7D%2C%20%7BitemCount%7D%2C%20%7Bitems%7D%20\(one%20line%20per%20item%20as%20%222%20x%20Margherita%20Pizza%22\)%2C%20%7Bnotes%7D%2C%20%7BdeliveryPostcode%7D%2C%20%7Btime%7D%20\(createdAt%20in%20TIMEZONE%2C%20HH%3AMM\)%2C%20%7BposName%7D%2C%20%7BposError%7D%2C%20%7BretryCount%7D.%20Format%20%7Btotal%7D%20from%20minor%20units%20with%20the%20CURRENCY%20environment%20variable%2C%20e.g.%20%2225.00%20GBP%22.%20Never%20put%20the%20customer's%20email%2C%20full%20surname%20or%20full%20phone%20number%20in%20a%20message.%20If%20a%20placeholder%20is%20null%2C%20replace%20it%20with%20%22-%22.%0A%0AMY%20RULES%20\(create%20these%20as%20the%20starting%20set\)%0A%0A-%20%22Big%20order%22%3A%20order.created.v2%2C%20minimum%20total%20100.00%2C%20Slack.%20Template%3A%0A%20%20*Big%20order%20%7Bcode%7D%20%E2%80%94%20%7Btotal%7D*%0A%20%20%7BorderType%7D%20%C2%B7%20%7BitemCount%7D%20items%20%C2%B7%20%7Btime%7D%0A%20%20%7Bitems%7D%0A-%20%22Delivery%20placed%22%3A%20order.created.v2%2C%20order%20type%20in%20Delivery%2C%20CateringDelivery%2C%20Slack.%20Template%3A%0A%20%20*Delivery%20%7Bcode%7D%20%E2%80%94%20%7Btotal%7D*%0A%20%20%7BdeliveryPostcode%7D%20%C2%B7%20for%20%7Btime%7D%0A-%20%22Allergy%20note%22%3A%20order.created.v2%2C%20notes%20contain%20allergy%2C%20allergic%2C%20intolerance%2C%20coeliac%2C%20Slack.%20Template%3A%0A%20%20*Allergy%20note%20on%20%7Bcode%7D*%0A%20%20%7Bnotes%7D%0A-%20%22Till%20failure%22%3A%20order.pos.dispatch.failed%2C%20channels%20Slack%20and%20SMS%20\(SMS%20skipped%20until%20Twilio%20is%20configured\).%20Template%3A%0A%20%20TILL%20FAILURE%20%7Bcode%7D%20%7Btotal%7D%20%E2%80%94%20%7BposName%7D%3A%20%7BposError%7D.%20Enter%20this%20order%20by%20hand.%0A%0ADEFINITION%20OF%20DONE%0A%0A-%20%5B%20%5D%20A%20signed%20order.created.v2%20for%20a%20%C2%A3120%20pickup%20order%20sends%20exactly%20one%20%22Big%20order%22%20Slack%20message%20and%20nothing%20else.%0A-%20%5B%20%5D%20The%20same%20event%20delivered%20twice%20sends%20one%20message%2C%20not%20two.%0A-%20%5B%20%5D%20A%20bad%20signature%20returns%20400%20and%20sends%20nothing.%0A-%20%5B%20%5D%20A%20%C2%A330%20Delivery%20order%20with%20the%20note%20%22severe%20nut%20allergy%22%20fires%20%22Delivery%20placed%22%20and%20%22Allergy%20note%22%2C%20not%20%22Big%20order%22.%0A-%20%5B%20%5D%20An%20order.pos.dispatch.failed%20event%20fires%20%22Till%20failure%22%20on%20Slack%2C%20and%20on%20SMS%20if%20Twilio%20is%20configured.%0A-%20%5B%20%5D%20Rules%20edited%20in%20%2Fsettings%20take%20effect%20on%20the%20next%20event%20without%20a%20redeploy.%0A-%20%5B%20%5D%20The%20example%20payload%20in%20the%20context%20\(a%20%C2%A325.00%20delivery%20with%20the%20note%20%22Ring%20doorbell%22\)%20fires%20only%20the%20%22Delivery%20placed%22%20rule%2C%20once.%0A-%20%5B%20%5D%20A%20Slack%20URL%20that%20returns%20an%20error%20shows%20as%20failed%20in%20the%20history%20after%20three%20attempts%2C%20and%20the%20app%20keeps%20accepting%20events.%0A%0AWHAT%20TO%20GIVE%20ME%20WHEN%20YOU%20ARE%20DONE%0A%0A1.%20The%20public%20webhook%20URL%20\(%E2%80%A6%2Fwebhooks%2Fstorekit\)%20and%20the%20%2Fsettings%20URL.%0A2.%20The%20environment%20variables%20to%20set%20\(STOREKIT_WEBHOOK_SECRET%2C%20ADMIN_PASSWORD%2C%20SLACK_WEBHOOK_URL%2C%20CURRENCY%2C%20TIMEZONE%2C%20the%20three%20Twilio%20variables\)%20and%20where%20to%20set%20them%20on%20this%20platform.%0A3.%20Confirmation%20that%20the%20backend%20stays%20up%2024%20hours%20a%20day%20and%20does%20not%20sleep%20between%20requests.%0A4.%20How%20to%20run%20the%20%22Send%20test%22%20button%20and%20what%20I%20should%20see%20in%20Slack.%0A)

[![Open in Replit](https://replit.com/badge?caption=Open%20in%20Replit)](https://replit.com/?stack=Build\&prompt=JIAg7ghgdgLiEgM4FsIBs3zQUwE4wEsoBzeABzJADMB7XEZATxF20RggFddoYA6JDDrYA1gTiJsUACaIQ4+OGwAjABY0aI8KqnYAbnnhQQdaYYJyyaCAGNs0k-WiOz9KhAJo5QltluqGZkIMABojBwUzGwIzOTAdGB0nKEYaKGwQdPtvGhARKBowcJAYbAxvHWQTYwBlaxstOhBlIOwAD34QYDhkTnZM-UNkCzlndDwYMPSDekkZEp0kCGQM8fwSsAI7MOgHaaGIEQz2YTE4SAVaJ18rRj4AKHuAIU5PCPPxAIRlWyP5xIgcFUEFGIDInGUaC2IAAEgAVOEABRqIAAqgAlAAy1CaiQyYBU6k0IAAFGYrDRmApdiUypgVgsMhjMQBKHZIVAYEDSQEQH6SHH0XCcHCjeaqCxCXCMHbzBCSGCEEiWCDEDKgGzOPoZKi4GhVJhg9TpB73ADCAHkAHJwgCiAA04SAAIJPC2op01OEW9G2gDSwDhj1AwuMCHUiDI4nQ4mYyj6RDYcjSglOChJJ1YZz4Nn1bKM1Sh6RceCIpBpZAgjBWsEs1hgVyqV187C4PFrYU1VAAl2KHD9cIgBJnRApNcYCoQqMxDYUwxR4MoaJw4IkyxVASBgRQpPJjIaR81mHNpGWlGoNCIh48YYVU1mFASL5o4nQRAAue4AWhAtv8IEGWB5DFWEEUREBEQtL1wE+RQACkamtZoaGkIJcgQZkul8YhJXMYw8XvUc4B5RBVCXCBcGkB4fzhRYl1QrcQUZJBtwyABvEAACJAJgTj3y4gAeHiSkYMhsAAPk4sJOJ5Dg+JADi+CUkAAF9VIEOEwFydo7DIQg0kQfiCJwYhbGYUw8BzVhAXsACDCAslcknEBtXkGAAG4XIFCzcCsvxSiovQACYWSQKRZGYgcMh8kxlAAK2wGxJmKAifL4VgqE4GQ-JshwAHoS18jKsqoxBOBsOx7Fsgq0uK7L3E8WyeMsFdCs7PohBWXAwnszhVnmOq9naVdtPsuAiHYPwHBoKg90QGIMlkiBqJAdFsAARz69hRlYJACGIdIHE2RIQBqPQCDaARbRmZhWE2tg4E1XBcAINhmJ0CBXDkRBzraL8YjCH6Lq-QgVlbZBKBpIG-vmg7AW4bABAANVLadGSqY6AlSqgqC2Ah0BAAADaHCZAKFlB4aVBRAVJuDJ6BiE4VVsDCBMSEUSQbFYOBQE2LkyD1c6zD3AspHOvUoBrOA9Ao-HIQyTUMFsr0fX9QMAH0AHVbSeGELQtP11ZqW0zV9OEBDWhKkqMRg13ZgE4AarwQBmAhcc1fTjEx0CkRAAAWAAGAOVoAWWYcKyBoIgeg6ltI-mb2ECCto2kEeG4k+IgQAARgAVjC3MZCHU6IAMZjhJ8Ailv5HUCEHZLWEjNIHCCoOwjxYxpFyAitNwLRdX1ZjEFL2zczIO5vxAC0oDsLDgTLxv47MFu28Is4YK5ZyTwWTdhNVDwoEB7vFl7rRen6ZzlAyNAaHYfjDz8GwAmExO05gPpSTEmQyzCLv0jCJ29hQo0jSGgGcFEtCRDSKsKgpR6A9zfIxUYSUmYYGPOVSqy8BDTzTvgTgkN5gpgQKDPAYQowNBcgQ22Ep2bsE8JgL+p52ZNEAQ4Gk3MqbiAEFaQY9Au7MVPoWKk+5sDIDoMwCAsDDCL2bmWFatFjjLAyMJccJgKoggIGkdAYDmiLTKAQGYtkxG7QBMYNIdgBCogFARaG-0HAfVcPACoGQsoEHunkbAzBmw2JHg4Hi-EECsDEpuGaosxpYGsgxYeBgIhyH2gUVg0gwjxBsjMNy30ODv2vD+Z0KQwTM0cABGQkdo5gj1KUJKtkWiKErIgRAvcHDnzgFfMKuAYkmDSfCJEKJQGME8okuuiU4BNGKgKKwB8faIlDtArxr00ARRJEIDgaB24EDIL-fRN0ABi2AWbcgsLmLKMA4Q0GWWEcQoiylbD2WI08uNDAC2ucA3a0dsBqkHCLYYCSXJQHEIZEAQVc5BwYH4KAcgADFgK+BBxWj5OEokMgWGqNFWaAARTZeAZQQS2CIfBYRgBQBqFKPZZpuDKHmmYMIZobIvRIOiqEN0qU0rLIiHF+DClPHoZWCeP4ODy2AkIspbApCOyaEQL8h4fKIE8mYBlmLnTSGkI3PsYIcWojIHCAgDJenU1lQYzFhVi6ax0PhHQzB+HOQgBQHRBEjhUmTMMRU9gj4lGsnABAww6lnjtUPJRTjMgijQAIScb0aQXOQIgAA2gAXT4CGkAqiWlQEDYU0Rek7gJo6vqSyuYIbQEYFaP1NIbBZq6nwGWMArScGQFfJwO4KKCviLuMq1rXpUUeLaNoywrDRUojmt19hy1BXyYwG+n1Pz3DYvcEAXEeLyU4mlLm-lB3BSktOriS15JTpnTOziMR52mHVnyGw2cgoAGY107q4rmMw87nTZyeEFS9O7OIgitfJGAwoWbrt3Us9A8lAVtx-VxQgZAANApCMBmSGLpQ7OwABgAnAhyDV6ZIHOXLAE5yz5IBxQy+uFCL530v1dKZ9u7yEiHVZqlY87W5BT9l+AO2cvx5zhNnAO74-a53fEHAAWmR69wIoDpDQPO7WTwBOcUjlWmteB5LJtCFBpduVnS8X4pxOjDGmMsdzmxjjZ6ON8ck3qm61H4Pqc04x5jrHs7Zx44ZgO-G8O7pDYged6IzxdzoFfDAkmS0nC6lu4Du7cb10LTR9TcEaCqCgAJ3d1h2DhfM1xVFNB4POZfaIjwon1NxWi1AAAAu0btOAcz6ji1xMgxpkucQANR+z9qes9XGABsAB2AAHAhirnFUB92wEqYgZoDIio-V+4DKkMt7tKBG+SkbgsKQWy5pR86Q4UWIEkaM2KABe22IAVfI7SytK31NrdwBt0sckMuoceXYeSp6gNXtQ5tXgsZ7vXZfVYTg8nA0ffI7fYA0gfuKaey+jgbR0Q2QA7hpbXEQ3A5WbD3rKE3avUHHN2HM6OKcSgCdrinbP0ICfrsyQUlKtHaS-Os0OhhVk6ky9O7-E864a4i92Ab2mfSS+wjrnAOgf8QUyskDEAIdQ-4q3VSsPo0LZUsB6NU3erJe3U9vd-Oc7nr9h9nHePOJh1Wg9NsvAeuIDQJwYg86mBfkbhwbgRutefSVUmILoPd3sFYAN7O86msgDW1nIlB2uJu92TAJ96mA+cWiDARg87MTNzSOHyO7Bhu3vU5rbOzoc7OmdOHw5sBSPqdRH6HP+pKwpEp6d5ga1Wy26ApiGAQO-vXo0JRIgNk3P8Wx-WcQnAU8gFztnPgQK2ua64jfEg3fe+Mb4KezrkvQey6vZNqDJn5WKuVc7lXQePfzta1PQ4LoDDG8-cH0PXEtn1gBT1yP0f1Ox5kPHrXieYDJ5q7abOIAWvOitFfjDn6b9cULx61cx5wWwj2byYTby3VH0BAn2S370HzzlzmkjHxwnfkn2DgDjayCmHQXx3VwNl1l3uAUWoBTShH6FCQrgRRVW5THQiiRU3FUEVDIEMjyjyhHGzFzTyi7hsEQC4MGBvjEkHDyifCJCvGEMJEvC-GakeDhBhFtFOhNmtFRV-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-k2I6jJsFJmjAm2ICMxIQn-NOo8Atk65EslEQnw0vjYAEmZtEA1JtLiw-r6r6Bw1-UNMQBU4zsLsXoOAds9tOJ8w2JXMQmJrGBERKqAmwg2ISF0nlMApVMRYbK7KHKrRbQwgLLTLKnE8ksQnE9bR8GQmPikmYAVIBBLrcBrqgnEK0A1IB4DRcL6B8Ku6sZFgzQMRfQrQzQABNIpcWNIKWF2WWI2ievgYgAQDTRO4FJOp4RETiPhtJcEG6zNALPAOKrLTwABFNMqXANYwpURhharYW2tEWZ8upZmNBhAZx7AVxxxJFQXX+7AJFo+2CTiL8N5oxk59EVETEW0FEEkNpjIPEAUQ6xYVsfAM8BUFkR4H8TiTlUgEojQvEilqCsIWC3oKoZonOGFVeR6kQDSexmyT8GdAAKjZbqv8ZQmwDUlZOaeWZUmlfXVCfSPhXCZAAAHaFJEnf81Jw05ADXmnTjcC2JTXZcWXiNkqkW1cILl0eW6rKLEVjB7XMUmU4EywvXpQryxrxWIYHGpWQBpX-XmAFWzBlWlnll1XNXan6n2AAn9XqYLWVhbWuJnQlZztmB4dOXXA8yV0gophc65BC4wawxc3iAsVtE8AcIbBzlYAaBYjoBtgML9FbBA3fhg33zsAw3pWc3Yja3UTopjBo2lWNWsdqnJ5OJNUuQzTWB5JXKSzdCcjPKOH-68rnZRXihNbSQD3EAbaxIHAjlPBzG+ZkakVC5cZGZElzq4QJXSgw2bLMRsQtlnQKSMR5DJ21JVX42s6FJhn4mO8xmJmrpYBDA1xkxWrqkhN217hUVbQtlq7AxMarQp4tkQBUVHLmWQBI0QBo0XQeTDpczuWCzmwEBwUHtVUGh2UYpzCAIu0kodEDzWX9pCoN6927rLb5hdGzwDGVpCPiPiDh4GRhIJrbJqHrkwoi5AbeO9lnJqGaBhOiOSOfgHBYZcc+S567SQW5BA4X7AzwoFK+ySA1PiOM9wUDMcOwGYoBTFh41OJJBDEA1AqR3o8-a5BOJI2wR6h7AN6aROJh2G383c6ydnIOP2X0j8WfwROXQzE8TMjSyPKDCOH2ygJQs3p53fal34NwrRWF6UwD23ZL2kbcgb20g72EZ214v1P0R-S5BJ6AoRZ07FRM6OBT7jqIjrYUwCJ0gOhMuPhEgsTAkqoKQeUCP1PiDisQ2aqqxaCRYCJK3hpSRqOoVgVamYIFGnPc6uIPN2YvNcAfNRNQpsugSbVFhfOwHHWN6-7qgLFJ4EuM893MJFGcGDO8Hdi76wBRg5AvKTbFhASpQJEpF4FVASHAQ3yGGUpFgrVKAjhMXkFdJBthvrx7gCSiTzGLQQAk7gBVCfd5CCTaajn3QXRfQcO8P7gaTiDwRIRoQRDLw0QsRSRAAyAnpNEN4KZOAX+EWHa-NpZ8xAeGHPMcWDFjrnOY7IVrlnHJ8AVEPe9FlI1l2v1kNmNlNlTrCArqrprrrobqbvRCJtOk2u2oVN1jV-VmZCpX2bUOOeJtsvsscsYeId2XK7lsucVvlkQF56OiSEpbMIG3RnFsBP88BEbAeBGpABAtC2uuOP3sil+FXtbHBPZXoy3GXA+QQB5ClIcC7jei3hwExd0Wod2WMD1IemvEhthGvuwaymYmXtXtKHXs3qEDDATmBB5jvpFC07d79yDfuCAA\&referrer=docs.storekit.com)

Either button opens the builder with the whole prompt below already filled in. Nothing starts until you press send, so read it through and edit the parts marked as yours first.

> **Volledige prompt (klik om uit te klappen)**
>
> ```text theme={null}
> I want a small alerting app for my restaurant. storekit sends it a webhook whenever an order is placed or an order fails to reach my till, and it decides whether anyone needs to know and tells them on Slack or by text. It must never miss an alert, never send the same alert twice, and never make storekit wait for a reply.
>
> Build it with a backend that has a public HTTPS URL for the webhook (deploy it and tell me the URL), a small database for rules and history, and a settings page I can use from my phone.
>
> ```
>
> ```text theme={null}
> CONTEXT ABOUT STOREKIT
>
> I run a hospitality business on storekit (storekit.com), an online ordering and payments platform for restaurants, cafés and bars. storekit can notify my own app about things that happen in my store by sending webhooks.
>
> How storekit webhooks work:
> - Each event is an HTTP POST with a JSON body to a URL I register in the storekit dashboard.
> - The body has the shape { "event": "<event type>", "data": { ... } }. Two exceptions: the legacy order.created event (do not use it; use order.created.v2) sends the bare order object, and the order.refund.created / order.refund.succeeded / order.refund.failed events put order, customer, venue and refund next to event instead of inside data.
> - Requests are signed with Svix. Every request carries the headers svix-id, svix-timestamp and svix-signature. Verify them with the official `svix` library for your language, using a secret I will provide in an environment variable called STOREKIT_WEBHOOK_SECRET. Reject anything that fails verification with HTTP 400.
> - My endpoint must respond with a 2xx status within 15 seconds. Save the event to the database first, respond 200, then do the work from the saved copy.
> - Once I have responded 200, storekit will not send that event again, so the work must not be lost: store each event with a status (pending, done, failed) and only mark it done after the work has actually succeeded. On startup and on a timer, pick up anything still pending or failed and retry it. Never do the work only in memory after responding.
> - The same event can occasionally be delivered more than once. Use the svix-id header as the unique key for the saved event: a repeat of an event already saved is ignored, whatever its status.
> - Any page or endpoint protected by a password must be served over HTTPS only; redirect or refuse plain HTTP.
> - Money fields (total, tip, deliveryFee, discountTotal, item price, modifier price) are integers in minor units: 2500 means £25.00.
> - orderType is one of Delivery, Pickup, InStore, Curbside, CateringDelivery, CateringPickup or Billpay.
> - table is only present for in-store orders; deliveryAddress and pickUpTime only for delivery orders. When they do not apply the key is omitted, so treat a missing key the same as null. notes and items[].note can be null or empty. customer.companyName and customer.vatNumber appear only when supplied.
>
> Example order.created.v2 payload:
>
> {
>   "event": "order.created.v2",
>   "data": {
>     "id": "ord_abc123",
>     "code": "A1B2",
>     "asap": true,
>     "total": 2500,
>     "tip": 250,
>     "deliveryFee": 299,
>     "discountTotal": 0,
>     "orderType": "Delivery",
>     "pickUpTime": "2024-01-15T10:45:00Z",
>     "channel": "WEB",
>     "poNumber": null,
>     "createdAt": "2024-01-15T10:30:00Z",
>     "deliveryTime": "2024-01-15T11:00:00Z",
>     "notes": "Ring doorbell",
>     "customer": {
>       "firstName": "John",
>       "lastName": "Doe",
>       "email": "john@example.com",
>       "phone": "+44123456789",
>       "marketingConsent": true
>     },
>     "items": [
>       {
>         "name": "Margherita Pizza",
>         "printName": "Margherita",
>         "price": 1200,
>         "quantity": 1,
>         "plu": null,
>         "posId": null,
>         "taxRate": 20,
>         "note": null,
>         "modifiers": [
>           { "name": "Extra cheese", "printName": "Cheese", "price": 150, "quantity": 1, "plu": null, "posId": null, "taxRate": 20 }
>         ]
>       }
>     ],
>     "venue": {
>       "id": 1234,
>       "name": "My Restaurant",
>       "slug": "my-restaurant",
>       "address": {
>         "street1": "123 Main St",
>         "street2": "",
>         "city": "London",
>         "postCode": "W1A 1AA",
>         "country": "UK",
>         "companyName": "My Restaurant Ltd",
>         "coordinates": { "latitude": 51.5074, "longitude": -0.1278 }
>       }
>     },
>     "deliveryAddress": {
>       "street1": "456 Oak Ave",
>       "street2": "Flat 2",
>       "city": "London",
>       "postCode": "E1 6AN",
>       "country": "UK",
>       "notes": null,
>       "coordinates": { "latitude": 51.5155, "longitude": -0.0722 }
>     }
>   }
> }
>
> The full list of event types and payloads is at https://storekit.com/docs/developers/webhooks/webhook-events
> ```
>
> ```text theme={null}
> THE SECOND EVENT: order.pos.dispatch.failed
>
> storekit also sends this event when an order could not be pushed to my point-of-sale system. It has a different, smaller payload:
>
> {
>   "event": "order.pos.dispatch.failed",
>   "data": {
>     "order": { "id": "ord_abc123", "code": "A1B2", "total": 2500 },
>     "venue": { "id": 1234, "name": "My Restaurant", "slug": "my-restaurant" },
>     "pos": { "name": "Zonal", "error": "Connection timeout: Unable to reach POS endpoint" },
>     "retryCount": 3
>   }
> }
>
> This one is always urgent: it means the kitchen may not know about a paid order.
>
> WHAT TO BUILD
>
> 1. POST /webhooks/storekit — verify the Svix signature with the official svix library and STOREKIT_WEBHOOK_SECRET against the raw request body; 400 on failure. Save the event and return 200 immediately, then evaluate rules and send messages in the background. Ignore any svix-id already saved. Handle order.created.v2 and order.pos.dispatch.failed; save and acknowledge any other event type but do nothing with it.
> 2. A rules engine. Each rule has: a name, which event it applies to, conditions, one or more channels (Slack, SMS, or both), a message template, and an on/off switch. Conditions for order.created.v2: minimum total (in pounds, convert to minor units when comparing), order type is one of a list, notes contain any of a list of words (case-insensitive, also check each item's note field), and a time window (only alert between HH:MM and HH:MM in TIMEZONE). order.pos.dispatch.failed has no conditions: it always alerts on all of its rule's channels. Every matching rule fires on every channel it has; a rule fires at most once per order per channel (order id + rule id + channel is unique).
> 3. Slack delivery through an incoming webhook URL stored per rule (SLACK_WEBHOOK_URL as the default). Format the message with Slack's mrkdwn: bold first line, then one line per detail.
> 4. SMS delivery through Twilio using TWILIO_ACCOUNT_SID, TWILIO_AUTH_TOKEN and TWILIO_FROM_NUMBER, to one or more numbers stored on the rule. If the Twilio variables are unset, the SMS channel is shown as disabled in the settings page and is skipped, not errored; the rule's other channels still fire. Keep texts under 160 characters.
> 5. GET /settings — protected by HTTP basic auth with ADMIN_PASSWORD. Lists rules with on/off toggles, lets me add, edit and delete rules, and has a "Send test" button on each rule that fires it with fake data. Below the rules, show the last 100 alerts sent: time, order code, rule, channel, and whether delivery succeeded (store the Slack or Twilio response).
> 6. If Slack or Twilio return an error, retry three times with a pause between attempts, then mark the alert failed in the history. Never retry a successful send.
> 7. GET /health that returns 200 and { "ok": true } and nothing else.
>
> MESSAGE TEMPLATES
>
> Templates use placeholders in curly braces: {code}, {total}, {orderType}, {customerFirstName}, {itemCount}, {items} (one line per item as "2 x Margherita Pizza"), {notes}, {deliveryPostcode}, {time} (createdAt in TIMEZONE, HH:MM), {posName}, {posError}, {retryCount}. Format {total} from minor units with the CURRENCY environment variable, e.g. "25.00 GBP". Never put the customer's email, full surname or full phone number in a message. If a placeholder is null, replace it with "-".
>
> MY RULES (create these as the starting set)
>
> - "Big order": order.created.v2, minimum total 100.00, Slack. Template:
>   *Big order {code} — {total}*
>   {orderType} · {itemCount} items · {time}
>   {items}
> - "Delivery placed": order.created.v2, order type in Delivery, CateringDelivery, Slack. Template:
>   *Delivery {code} — {total}*
>   {deliveryPostcode} · for {time}
> - "Allergy note": order.created.v2, notes contain allergy, allergic, intolerance, coeliac, Slack. Template:
>   *Allergy note on {code}*
>   {notes}
> - "Till failure": order.pos.dispatch.failed, channels Slack and SMS (SMS skipped until Twilio is configured). Template:
>   TILL FAILURE {code} {total} — {posName}: {posError}. Enter this order by hand.
>
> DEFINITION OF DONE
>
> - [ ] A signed order.created.v2 for a £120 pickup order sends exactly one "Big order" Slack message and nothing else.
> - [ ] The same event delivered twice sends one message, not two.
> - [ ] A bad signature returns 400 and sends nothing.
> - [ ] A £30 Delivery order with the note "severe nut allergy" fires "Delivery placed" and "Allergy note", not "Big order".
> - [ ] An order.pos.dispatch.failed event fires "Till failure" on Slack, and on SMS if Twilio is configured.
> - [ ] Rules edited in /settings take effect on the next event without a redeploy.
> - [ ] The example payload in the context (a £25.00 delivery with the note "Ring doorbell") fires only the "Delivery placed" rule, once.
> - [ ] A Slack URL that returns an error shows as failed in the history after three attempts, and the app keeps accepting events.
>
> WHAT TO GIVE ME WHEN YOU ARE DONE
>
> 1. The public webhook URL (…/webhooks/storekit) and the /settings URL.
> 2. The environment variables to set (STOREKIT_WEBHOOK_SECRET, ADMIN_PASSWORD, SLACK_WEBHOOK_URL, CURRENCY, TIMEZONE, the three Twilio variables) and where to set them on this platform.
> 3. Confirmation that the backend stays up 24 hours a day and does not sleep between requests.
> 4. How to run the "Send test" button and what I should see in Slack.
> ```

Open `/settings`, druk op "Send test" bij de "Big order"-regel en controleer of het bericht in Slack aankomt voordat je storekit verbindt.

## Stap 2: Verbind storekit

**1. Open the webhooks portal**

In the dashboard sidebar, go to **Settings** → **Webhooks**. If your plan includes webhooks and they are not yet enabled, click **Enable Webhooks**. If the page shows **Upgrade to enable webhooks**, [contact support](/docs/getting-started/contact-support) to discuss adding webhooks to your plan.

**2. Add your app as an endpoint**

In the embedded portal, add a new endpoint, paste the webhook URL your app builder gave you, and tick only the event types the recipe needs.

**3. Copy the signing secret into your app**

Open the endpoint you just created and copy its signing secret. In your app builder, save it as the environment variable `STOREKIT_WEBHOOK_SECRET` (builders call this "secrets" or "environment variables"). Redeploy if the builder asks you to.

**4. Send a test event**

Use the portal's test option on the endpoint to send a sample event. The delivery log shows the response code from your app. A `2xx` means your app accepted it; anything else, read the response body in the log — it is usually a missing secret.

Vink voor dit endpoint `order.created.v2` en `order.pos.dispatch.failed` aan.

## Stap 3: Test met een echte bestelling

Plaats een bestelling op je eigen storefront boven de drempel voor "Big order" (of verlaag de drempel voor de test naar £1 in `/settings`). Het Slack-bericht moet binnen enkele seconden na de orderbevestiging aankomen. Zet de drempel daarna terug.

Om de till-failure-melding te testen zonder je kassa te breken, gebruik je de testoptie in de webhooks-portal en kies je `order.pos.dispatch.failed` als eventtype.

## Ideeën

* **Verschillende kanalen per shift**: voeg een dag-van-de-week-conditie toe en stuur weekendmeldingen naar een ander Slack-kanaal.
* **Dagelijkse telling**: een regel zonder condities die een teller verhoogt, en om 22:00 uur een samenvattend bericht.
* **Lage beoordelingen**: voeg `order.rating.updated` toe en meld de GM wanneer een beoordeling 2 sterren of lager is.
* **Printer offline**: voeg `printer.status.offline` toe en sms wie er dienst heeft; de payload bevat de printernaam op `data.printer.name`.

## Wat dit niet doet

* **Het vervangt storekits eigen meldingen niet.** Ordergeluiden in het dashboard, printer-offline-e-mails en POS-foutafhandeling blijven werken zoals voorheen.
* **Het kan een bestelling niet accepteren, weigeren of repareren.** Webhooks zijn eenrichtingsverkeer. De melding vertelt een persoon; de persoon handelt in het dashboard.
* **Tekstberichten zijn afhankelijk van Twilio**, inclusief de aanmeldcontroles en prijs per bericht. Slack is gratis en direct, en daarom gebruiken de startregels Slack.
* **Het is jouw service.** storekit-support kan bevestigen dat een event is afgeleverd bij je URL en de responscode; de regels en berichten zijn van jou.

## Gerelateerd

> **[Slack-meldingen](/docs/nl/developers/webhooks/slack-integration)**
>
> Elke bestelling rechtstreeks naar Slack zonder code.

> **[Een prompt schrijven die werkt](/docs/nl/guides/build/prompting-guide)**
>
> De structuur achter deze prompt, en wat te zeggen als de builder het mis heeft.

> **[order.pos.dispatch.failed](/docs/nl/developers/webhooks/webhook-events#order-pos-dispatch-failed)**
>
> De payload achter de till-failure-melding.

> **[Live bestellingen beheren](/docs/nl/guides/orders/managing-live-orders)**
>
> Wat te doen in het dashboard zodra de melding binnenkomt.


## Related topics

- [Slack-integratie](/docs/nl/developers/webhooks/slack-integration.md)
- [Uitverkocht-log en ochtenddigest](/docs/nl/guides/build/recipes/out-of-stock-log.md)
- [Live-orderbord voor de pass](/docs/nl/guides/build/recipes/live-order-board.md)
- [Bouw je eigen tools op storekit](/docs/nl/guides/build/overview.md)
- [Foodtrucks en pop-ups](/docs/nl/guides/use-cases/food-trucks.md)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.