> ## 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.

# Bestell-Alerts an Slack oder Ihr Handy

> Slack- oder SMS-Alerts bei großen Bestellungen, gebuchten Lieferungen oder fehlgeschlagenen POS-Übermittlungen, mit storekit-Webhooks und Regeln.

Eine kleine App, die Ihre Bestellungen beobachtet und der richtigen Person von denen erzählt, die wichtig sind: große Bestellungen, Lieferungen und Bestellungen, die es nie zur Kasse geschafft haben. Regeln sind Ihre Sache – bearbeitbar auf einer Einstellungsseite, ohne Re-Deploy, um einen Schwellenwert zu ändern.

> [!TIP]
>
> Wenn Sie nur jede Bestellung in einem Slack-Kanal ohne Regeln posten möchten, müssen Sie nichts bauen: Das storekit-Webhooks-Portal kann direkt an Slack posten. Siehe [Slack-Benachrichtigungen](/docs/de/developers/webhooks/slack-integration). Bauen Sie dieses Rezept, wenn Sie Filter, SMS oder unterschiedliche Personen für unterschiedliche Events brauchen.

## So funktioniert es

1. storekit sendet `order.created.v2` und `order.pos.dispatch.failed` an Ihre App.
2. Ihre App prüft jedes Event gegen die von Ihnen gesetzten Regeln (Bestellwert, Bestelltyp, Schlüsselwörter in Notizen) und wählt einen Kanal.
3. Sie postet über einen Incoming-Webhook in Slack und sendet optional eine SMS über Twilio.

## Was Sie brauchen

* Webhooks in Ihrem storekit-Plan aktiviert (**Settings** → **Developers**).
* Ein [Lovable](https://lovable.dev)- oder [v0](https://v0.dev)-Konto. Beide brauchen ein Backend für den Webhook-Endpoint – der Prompt bittet den Builder, eines zu deployen und Ihnen die URL zu geben. [Replit](https://replit.com) funktioniert ebenfalls und ist die einfachere Wahl, wenn der Builder beim Backend-Schritt Schwierigkeiten hat.
* Eine Slack-Incoming-Webhook-URL für den Kanal, in dem Sie Alerts haben möchten (erstellen Sie eine über die „Incoming Webhooks"-App im Slack-App-Verzeichnis, wählen Sie den Kanal und kopieren Sie die URL).
* Optional, für SMS: ein [Twilio](https://twilio.com)-Konto mit Telefonnummer. SMS kosten Geld pro Nachricht; Slack ist kostenlos.

## Schritt 1: Die App bauen

Fügen Sie den gesamten Block in einer einzigen Nachricht in den Builder ein. Passen Sie zuerst die Beispielregeln unter **MY RULES** an Ihr Geschäft an.

[![](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.

> **Vollständiger Prompt (zum Ausklappen klicken)**
>
> ```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.
> ```

Öffnen Sie `/settings`, drücken Sie „Send test" bei der Regel „Big order" und prüfen Sie, ob die Nachricht in Slack ankommt, bevor Sie storekit verbinden.

## Schritt 2: storekit verbinden

**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.

Haken Sie für diesen Endpoint `order.created.v2` und `order.pos.dispatch.failed` an.

## Schritt 3: Mit einer echten Bestellung testen

Geben Sie in Ihrem eigenen Storefront eine Bestellung über dem „Big order"-Schwellenwert auf (oder senken Sie den Schwellenwert für den Test in `/settings` auf 1 £). Die Slack-Nachricht sollte innerhalb weniger Sekunden nach der Bestellbestätigung ankommen. Setzen Sie den Schwellenwert danach wieder zurück.

Um den Kassenausfall-Alert zu testen, ohne Ihre Kasse zu beschädigen, nutzen Sie die Test-Option im Webhooks-Portal und wählen Sie `order.pos.dispatch.failed` als Event-Typ.

## Ideen

* **Unterschiedliche Kanäle je Schicht**: Fügen Sie eine Wochentag-Bedingung hinzu und leiten Sie Wochenend-Alerts an einen anderen Slack-Kanal.
* **Tageszählung**: Eine Regel ohne Bedingungen, die einen Zähler hochsetzt, und eine 22-Uhr-Zusammenfassung.
* **Niedrige Bewertungen**: Fügen Sie `order.rating.updated` hinzu und alarmieren Sie den GM, wenn eine Bewertung 2 Sterne oder weniger hat.
* **Drucker offline**: Fügen Sie `printer.status.offline` hinzu und schicken Sie eine SMS an die diensthabende Person; der Payload hat den Namen des Druckers unter `data.printer.name`.

## Was dieses Rezept nicht tut

* **Es ersetzt nicht storekits eigene Alerts.** Bestell-Sounds im Dashboard, Drucker-Offline-E-Mails und die POS-Fehlerbehandlung laufen wie bisher weiter.
* **Es kann eine Bestellung nicht annehmen, ablehnen oder reparieren.** Webhooks sind einseitig. Der Alert informiert eine Person; die Person handelt im Dashboard.
* **SMS hängen von Twilio ab**, einschließlich der Anmeldungsprüfungen und der Preise pro Nachricht. Slack ist kostenlos und sofort verfügbar, weshalb die Startregeln Slack verwenden.
* **Es ist Ihr Service.** Der storekit-Support kann bestätigen, dass ein Event an Ihre URL zugestellt wurde und mit welchem Antwortcode; die Regeln und Nachrichten gehören Ihnen.

## Verwandt

> **[Slack-Benachrichtigungen](/docs/de/developers/webhooks/slack-integration)**
>
> Jede Bestellung direkt an Slack, ohne Code.

> **[Einen Prompt schreiben, der funktioniert](/docs/de/guides/build/prompting-guide)**
>
> Die Struktur hinter diesem Prompt und was zu sagen ist, wenn der Builder es falsch macht.

> **[order.pos.dispatch.failed](/docs/de/developers/webhooks/webhook-events#order-pos-dispatch-failed)**
>
> Der Payload hinter dem Kassenausfall-Alert.

> **[Live-Bestellungen verwalten](/docs/de/guides/orders/managing-live-orders)**
>
> Was im Dashboard zu tun ist, sobald der Alert ankommt.


## Related topics

- [Out-of-Stock-Log und Morgen-Digest](/docs/de/guides/build/recipes/out-of-stock-log.md)
- [Eigene Tools auf storekit bauen](/docs/de/guides/build/overview.md)
- [Slack-Integration](/docs/de/developers/webhooks/slack-integration.md)
- [Live-Bestell-Board für den Pass](/docs/de/guides/build/recipes/live-order-board.md)
- [storekit-Dokumentation: Online-Bestellung, Guides und APIs](/docs/de/index.md)


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