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

# Order Alerts to Slack or Your Phone

> Send a Slack message or text when a big order lands, a delivery is placed or a POS dispatch fails, using storekit webhooks and a small rules engine.

A small app that watches your orders and tells the right person about the ones that matter: large orders, deliveries, and orders that never made it to the till. Rules are yours to edit from a settings page — no redeploy to change a threshold.

<Tip>
  If you only want every order posted to a Slack channel with no rules, you do not need to build anything: storekit's webhooks portal can post straight to Slack. See [Slack Notifications](/docs/developers/webhooks/slack-integration). Build this recipe when you need filtering, texts, or different people for different events.
</Tip>

## How It Works

1. storekit sends `order.created` and `order.pos.dispatch.failed` to your app.
2. Your app checks each event against the rules you set (order value, order type, keywords in notes) and picks a channel.
3. It posts to Slack through an incoming webhook, and optionally sends a text through Twilio.

## What You Need

* Webhooks enabled on your storekit plan (**Settings** → **Developers**).
* A [Lovable](https://lovable.dev) or [v0](https://v0.dev) account. Both need a backend for the webhook endpoint — the prompt asks the builder to deploy one and give you its URL. [Replit](https://replit.com) also works and is the simpler choice if the builder struggles with the backend step.
* A Slack incoming webhook URL for the channel you want alerts in (create one from the "Incoming Webhooks" app in Slack's app directory, choose the channel and copy the URL).
* Optional, for texts: a [Twilio](https://twilio.com) account with a phone number. Texts cost money per message; Slack is free.

## Step 1: Build the App

Paste the whole block into the builder in a single message. Change the example rules under **MY RULES** to match your business first.

<div className="sk-build-btns">
  <a className="sk-build-btn sk-build-btn--lovable" href="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%20always%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.%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%20and%20deliveryAddress%20can%20be%20null.%20notes%20can%20be%20null%20or%20empty.%0A%0AExample%20order.created%20payload%3A%0A%0A%7B%0A%20%20%22event%22%3A%20%22order.created%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%22Pickup%22%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%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%20null%2C%0A%20%20%20%20%20%20%20%20%22modifiers%22%3A%20%5B%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%22table%22%3A%20%7B%0A%20%20%20%20%20%20%22id%22%3A%20%22tbl_123%22%2C%0A%20%20%20%20%20%20%22name%22%3A%20%22Table%205%22%2C%0A%20%20%20%20%20%20%22covers%22%3A%204%2C%0A%20%20%20%20%20%20%22posId%22%3A%20%22pos_tbl_5%22%2C%0A%20%20%20%20%20%20%22area%22%3A%20%7B%20%22id%22%3A%20%22area_1%22%2C%20%22name%22%3A%20%22Main%20Floor%22%2C%20%22posId%22%3A%20null%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%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%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%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%20notes)%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%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%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%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%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%20pickup%20with%20the%20note%20%22Ring%20doorbell%22)%20sends%20nothing%20with%20the%20starting%20rules.%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" target="_blank" rel="noopener noreferrer" aria-label="Build with Lovable"><img src="https://mintcdn.com/storekit/IGeB_84apyho3Z6U/images/logos/lovable.svg?fit=max&auto=format&n=IGeB_84apyho3Z6U&q=85&s=cc21d11769d5e6b686fed56390a88f5e" alt="" width="20" height="20" noZoom data-path="images/logos/lovable.svg" /> Build with Lovable</a>

  <a className="sk-build-btn sk-build-btn--replit" href="https://replit.com/?stack=Build&prompt=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-juL2CsFPbTBFa6QD-ppsMyDvEjO+JgEyu1NnC3IbtZiAmaRnMY3YBe2ldqTq02dHTZ0zpnOBNgNKBFqI-Rhf1JWFIbaJNSd0q2TTQFMQwEIyR6lGhKJECYS+5SvF6ziE4H5kA9NaZ8GKdukTxW0g4TjuV51fBSV7o0mxu9XG9k4H02qwTorIRHTXYe8TCK4R8hwBVw9uYZiFZEx+ni3mCMIu80dGAQ36aHtSRBoS3E8WEbtTt4bsleJjbtZZ4wXig50FO0t-Dh3Mg3I61S+90rDk-2Oac85fWWV2ZMwijdm6daHBdAYeTXL7OOd4tdzcTLRPRA+Qi9zMhPOie8zAXzEnbS0xAJu50VoZuaq5YwKLMWEe5dngVrFxXASlfK5V6rDN6ZySDiQenMGWtw23cTYmL2cXsdhex9j9wb7UGe1CfoRCuZNL7Dwxg4KHEWHgECRUZACkAHoNcjmzLmZAGuZY2EQAbwYQdJKDg10+IkV5LeEkvF+fiiBHhwhhLaWy91rRtN-NlW0NohJIu83wU8kZAQRz4AvaQjwdfUi8LkE8FRldRx0GGIBlFDCBPsZkUJzQMjglIrtHwhpBGwC-KVL8qspuIGYqUZAAhuhbhBIoeJVA8BSGFigd+iSqyK8hdiuFJDr2IrT4CwPwfKwwDDxH5l8leS-aH64GnB2CWUSJcoEl5K7s0ok-Sxld3+UDolSUt7aqeMwf2wN4Tclzt-uYCljT7YBJyQU0plTjA1MybSwJDSXHvOL+vzxP1N+AdOSPAPUXABFC9KAdID6V+fCMNbAZcGAISVEdKfZHwchCOCCKCfhdGTGWAHib-aVZeRgC9IJa9MldFYXZ3CUFpZpRiKsOQbgNUWAISBQFYaABBM4COXcVAZgO2fIO8PkRAyRDwVRYfU0E6GEZ0J0b0EAJ4VEYATENpR4KrLA6CW3Z8G3aPOAQAFAIQBgZ8oCI7IHIzILIMpTJIYSoyoKoqpkoGoCAmoKIT55gvQfR-RAxTpzpLprpbp7pHp4BiBXZ+gCIeAig3JDJ+h6IdFYYSkUwM5MoBAag1ZOYB9ihRpuBjBzZ5BkAVhTwmE85JZk8AI1pFMmEWARQ-Iko0ZSlpFeo5AaYCIfgGhiA9QgkqIuhaFTIvokBIEYgsByEGJVYVEBAYRdgpskVLUmEHAaQA9g4g8LBx9J9+17BdEhjVg5QGgCgwBM41RR4TA8R6AZdJJmgVxuRchJxx5SBIZV57hiYBAgthRRR+EcITRfx-xHiMhWJcFMg74GEJRMCxJqQKAoQ-IhBOxL5wZykwhi4+F-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&referrer=docs.storekit.com" target="_blank" rel="noopener noreferrer" aria-label="Open in Replit">
    <img src="https://replit.com/badge?caption=Open%20in%20Replit" alt="Open in Replit" height="36" noZoom />
  </a>
</div>

<p className="sk-build-btns__note">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.</p>

<Accordion title="Full prompt (click to expand)">
  ```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 always has the shape { "event": "<event type>", "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 and deliveryAddress can be null. notes can be null or empty.

  Example order.created payload:

  {
    "event": "order.created",
    "data": {
      "id": "ord_abc123",
      "code": "A1B2",
      "asap": true,
      "total": 2500,
      "tip": 250,
      "deliveryFee": 299,
      "discountTotal": 0,
      "orderType": "Pickup",
      "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",
          "price": 1200,
          "quantity": 1,
          "plu": null,
          "posId": null,
          "taxRate": null,
          "modifiers": []
        }
      ],
      "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 }
        }
      },
      "table": {
        "id": "tbl_123",
        "name": "Table 5",
        "covers": 4,
        "posId": "pos_tbl_5",
        "area": { "id": "area_1", "name": "Main Floor", "posId": null }
      },
      "deliveryAddress": {
        "street1": "456 Oak Ave",
        "street2": "Flat 2",
        "city": "London",
        "postCode": "E1 6AN",
        "country": "UK",
        "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 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: 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 notes), 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, minimum total 100.00, Slack. Template:
    *Big order {code} — {total}*
    {orderType} · {itemCount} items · {time}
    {items}
  - "Delivery placed": order.created, order type in Delivery, CateringDelivery, Slack. Template:
    *Delivery {code} — {total}*
    {deliveryPostcode} · for {time}
  - "Allergy note": order.created, 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 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 pickup with the note "Ring doorbell") sends nothing with the starting rules.
  - [ ] 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.
  ```
</Accordion>

Open `/settings`, press "Send test" on the "Big order" rule and check the message lands in Slack before connecting storekit.

## Step 2: Connect storekit

<Steps>
  <Step title="Open the webhooks portal">
    In the dashboard sidebar, go to **Settings** → **Developers** and click **Manage Webhooks** (it reads **Enable Webhooks** the first time). Webhooks are a subscription feature — if you see **Upgrade to enable webhooks**, [contact support](/docs/getting-started/contact-support) first.
  </Step>

  <Step title="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.
  </Step>

  <Step title="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.
  </Step>

  <Step title="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.
  </Step>
</Steps>

Tick `order.created` and `order.pos.dispatch.failed` for this endpoint.

## Step 3: Test With a Real Order

Place an order on your own storefront above the "Big order" threshold (or lower the threshold to £1 in `/settings` for the test). The Slack message should arrive within a few seconds of the order confirmation. Put the threshold back afterwards.

To test the till-failure alert without breaking your till, use the test option in the webhooks portal and pick `order.pos.dispatch.failed` as the event type.

## Ideas

* **Different channels per shift**: add a day-of-week condition and route weekend alerts to a different Slack channel.
* **Daily count**: a rule with no conditions that increments a counter, and a 10 pm summary message.
* **Low ratings**: add `order.rating.updated` and alert the GM when a rating is 2 stars or lower.
* **Printer offline**: add `printer.status.offline` and text whoever is on shift; the payload has the printer's `name`.

## What This Does Not Do

* **It does not replace storekit's own alerts.** Order sounds in the dashboard, printer-offline emails and POS failure handling carry on as before.
* **It cannot accept, reject or fix an order.** Webhooks are one-way. The alert tells a person; the person acts in the dashboard.
* **Texts depend on Twilio**, including its sign-up checks and per-message pricing. Slack is free and immediate, which is why the starting rules use it.
* **It is your service.** storekit support can confirm an event was delivered to your URL and the response code; the rules and messages are yours.

## Related

<CardGroup cols={2}>
  <Card title="Slack Notifications" icon="slack" href="/docs/developers/webhooks/slack-integration">
    Every order straight to Slack with no code.
  </Card>

  <Card title="Writing a Prompt That Works" icon="wand-magic-sparkles" href="/docs/guides/build/prompting-guide">
    The structure behind this prompt, and what to say when the builder gets it wrong.
  </Card>

  <Card title="order.pos.dispatch.failed" icon="code" href="/docs/developers/webhooks/webhook-events#order-pos-dispatch-failed">
    The payload behind the till-failure alert.
  </Card>

  <Card title="Managing Live Orders" icon="bell" href="/docs/guides/orders/managing-live-orders">
    What to do in the dashboard once the alert arrives.
  </Card>
</CardGroup>


## Related topics

- [Out-of-Stock Log and Morning Digest](/docs/guides/build/recipes/out-of-stock-log.md)
- [Build Your Own Tools on storekit](/docs/guides/build/overview.md)
- [Slack Integration](/docs/developers/webhooks/slack-integration.md)
- [Live Order Board for the Pass](/docs/guides/build/recipes/live-order-board.md)
- [Food Trucks & Pop-ups](/docs/guides/use-cases/food-trucks.md)
