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

# Tableau des commandes en direct pour le pass

> Tableau plein écran qui affiche chaque ticket ouvert et le déplace entre Nouvelle, En préparation, Prête et Terminée selon les événements storekit.

Un écran mural pour le pass, le bar ou le comptoir avant qui affiche les commandes ouvertes sous forme de cartes et les fait avancer à mesure que leur statut change dans storekit. De gros codes de commande, la liste des articles, un minuteur qui vire au rouge quand une commande a trop attendu, et vos propres choix quant à ce qui doit être affiché. Personne n'y touche : il suit le tableau de bord.

> [!TIP]
>
> storekit propose un [Kitchen Display](/docs/fr/guides/orders/kitchen-display) intégré que la cuisine peut utiliser pour valider les tickets. Construisez cette recette lorsque vous voulez un écran en lecture seule avec une mise en page différente — un tableau « prêtes » face client, une vue de comptoir qui n'affiche que les retraits, ou un écran de bar qui n'affiche que les boissons.

## Fonctionnement

1. storekit envoie à votre application les événements du cycle de vie de la commande : `order.created.v2` transporte la commande complète ; `order.preparing`, `order.ready_for_pickup`, `order.out_for_delivery`, `order.completed` et `order.canceled` ne transportent que l'id, le code et le nouveau statut.
2. Votre application conserve une carte par commande et met à jour sa colonne à partir de chaque événement.
3. Une page dans le navigateur se rafraîchit toutes les quelques secondes et affiche les cartes en colonnes.

## Ce dont vous avez besoin

* Les webhooks activés sur votre forfait storekit (**Settings** → **Developers**).
* Un compte [Lovable](https://lovable.dev) ou [v0](https://v0.dev). Les deux ont besoin d'un backend pour l'endpoint webhook et d'une petite base de données — le prompt demande au builder d'en déployer un et de vous fournir son URL. [Replit](https://replit.com) fonctionne également.
* Un écran avec un navigateur (un téléviseur avec un stick bon marché, un vieil iPad ou un portable).

## Étape 1 : construire l'application

Collez l'intégralité du bloc dans le builder en un seul message. Ajustez d'abord **MY BOARD** si vous voulez une variante face client ou boissons uniquement.

[![](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%20live%20order%20board%20for%20my%20restaurant%3A%20a%20full-screen%20web%20page%20that%20shows%20open%20orders%20as%20cards%20in%20columns%20and%20moves%20them%20along%20as%20storekit%20tells%20it%20about%20status%20changes.%20Nobody%20interacts%20with%20the%20board%3B%20it%20just%20stays%20accurate.%20storekit%20sends%20the%20status%20changes%20as%20webhooks.%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%20the%20current%20orders%2C%20and%20a%20front%20end%20designed%20for%20a%20TV.%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%20STATUS%20EVENTS%0A%0ABesides%20order.created.v2%2C%20storekit%20sends%20these%20events%20for%20the%20same%20order%20as%20it%20moves%20through%20the%20kitchen.%20order.preparing%2C%20order.ready_for_pickup%2C%20order.out_for_delivery%2C%20order.completed%20and%20order.canceled%20all%20have%20this%20smaller%20payload%2C%20with%20data.order.status%20set%20to%20the%20new%20status%3A%0A%0A%7B%0A%20%20%22event%22%3A%20%22order.preparing%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%22status%22%3A%20%22preparing%22%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%0A%20%20%7D%0A%7D%0A%0ASo%20the%20order%20id%20is%20data.id%20for%20the%20full%20payload%20and%20data.order.id%20for%20the%20smaller%20one.%20Write%20one%20function%20that%20returns%20the%20id%20and%20code%20from%20either%20shape.%20Treat%20canceled%20as%20%22remove%20from%20board%22%20and%20out_for_delivery%20as%20%22Done%22.%20There%20is%20no%20accepted%20or%20rejected%20event%3A%20an%20order%20stays%20in%20New%20until%20its%20first%20status%20event%20arrives.%0A%0AEvents%20can%20arrive%20out%20of%20order%20or%20twice.%20Store%20a%20rank%20per%20status%20\(created%200%2C%20preparing%202%2C%20ready_for_pickup%203%2C%20out_for_delivery%204%2C%20completed%204%2C%20canceled%209\)%20and%20never%20move%20a%20card%20to%20a%20lower%20rank%20than%20it%20already%20has.%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%3B%20update%20the%20board%20state%20afterwards.%20Ignore%20any%20svix-id%20already%20saved.%0A2.%20An%20orders%20table%20keyed%20by%20storekit%20order%20id%20with%3A%20code%2C%20status%2C%20rank%2C%20orderType%2C%20table%20name%20\(or%20null\)%2C%20customer%20first%20name%2C%20items%20\(name%2C%20quantity%2C%20modifier%20names%2C%20notes\)%2C%20order%20notes%2C%20createdAt%2C%20deliveryTime%2C%20venue%20name%2C%20last%20updated.%20The%20full%20order.created.v2%20payload%20fills%20or%20refreshes%20every%20field%3B%20small%20payloads%20update%20only%20status%2C%20rank%20and%20last%20updated.%20If%20a%20small%20payload%20arrives%20for%20an%20order%20the%20board%20has%20never%20seen%2C%20create%20a%20card%20with%20just%20the%20code%20and%20status%20so%20it%20is%20not%20lost.%0A3.%20GET%20%2Fboard%2F%3CBOARD_TOKEN%3E%20%E2%80%94%20the%20full-screen%20page%2C%20where%20BOARD_TOKEN%20is%20a%20long%20random%20secret%20from%20an%20environment%20variable.%20There%20is%20no%20login%20because%20it%20runs%20on%20a%20screen%20behind%20the%20counter%2C%20so%20the%20token%20in%20the%20URL%20is%20the%20only%20thing%20keeping%20strangers%20out%3A%20any%20request%20to%20%2Fboard%20or%20%2Fapi%2Fboard%20without%20the%20exact%20token%20gets%20404%20and%20nothing%20else%2C%20and%20the%20token%20must%20never%20appear%20in%20logs%20or%20error%20pages.%20Below%20I%20write%20%2Fboard%20for%20short%3B%20always%20include%20the%20token%20segment.%20The%20page%20must%20not%20include%20customer%20surname%2C%20phone%2C%20email%20or%20delivery%20address%20anywhere%20in%20the%20page%20or%20its%20data%20requests.%20Columns%2C%20left%20to%20right%3A%20New%20\(created\)%2C%20Preparing%2C%20Ready%20\(ready_for_pickup\)%2C%20Done%20\(completed%2C%20out_for_delivery\).%20Cards%20show%3A%20the%20order%20code%20very%20large%2C%20orderType%20and%20table%20name%2C%20the%20customer's%20first%20name%2C%20the%20item%20list%20as%20%222%20%C3%97%20Margherita%20Pizza%22%20with%20modifiers%20indented%20and%20notes%20in%20italics%2C%20and%20a%20timer%20showing%20minutes%20since%20createdAt.%20Cards%20are%20ordered%20oldest%20first%20inside%20each%20column.%20Cards%20in%20Done%20disappear%20DONE_MINUTES%20after%20they%20entered%20Done%20\(default%2010\).%20Cards%20for%20canceled%20orders%20disappear%20immediately.%0A4.%20The%20page%20refreshes%20its%20data%20every%205%20seconds%20without%20flicker%20\(fetch%20JSON%20from%20GET%20%2Fapi%2Fboard%2F%3CBOARD_TOKEN%3E%20and%20re-render%2C%20or%20use%20server-sent%20events\).%20If%20a%20refresh%20fails%2C%20keep%20showing%20the%20last%20data%20and%20show%20a%20small%20%22reconnecting%22%20label%3B%20never%20show%20a%20blank%20board.%0A5.%20Timer%20colours%3A%20default%20until%20WARN_MINUTES%20\(default%2010\)%2C%20amber%20until%20LATE_MINUTES%20\(default%2020\)%2C%20red%20after.%20For%20orders%20with%20a%20deliveryTime%20in%20the%20future%2C%20count%20down%20to%20deliveryTime%20instead%20and%20label%20it%20%22for%2011%3A00%22.%0A6.%20Design%20for%20a%201080p%20TV%20viewed%20from%20three%20metres%3A%20dark%20background%2C%20minimum%2028px%20body%20text%2C%20order%20codes%20at%20least%2096px%2C%20no%20hover-only%20information%2C%20no%20scrollbars%20%E2%80%94%20if%20a%20column%20overflows%2C%20shrink%20cards%20gracefully%20and%20show%20%22%2BN%20more%22%20at%20the%20bottom.%0A7.%20Filters%20via%20query%20string%20so%20I%20can%20point%20different%20screens%20at%20different%20views%3A%20%3Ftypes%3DPickup%2CDelivery%20shows%20only%20those%20orderTypes%3B%20%3Fcolumns%3DReady%20shows%20only%20that%20column%20with%20much%20bigger%20cards%20for%20a%20customer-facing%20%22ready%20to%20collect%22%20screen%3B%20%3Fvenue%3D%3Cslug%3E%20if%20the%20account%20has%20more%20than%20one%20venue.%0A8.%20GET%20%2Fhealth%20that%20returns%20200%20and%20%7B%20%22ok%22%3A%20true%20%7D%20and%20nothing%20else.%20Every%20other%20route%20must%20be%20either%20%2Fboard%2F%3CBOARD_TOKEN%3E%2C%20%2Fapi%2Fboard%2F%3CBOARD_TOKEN%3E%20or%20the%20webhook.%0A%0AMY%20BOARD%0A%0A-%20Timezone%3A%20TIMEZONE%20environment%20variable%3B%20show%20all%20times%20in%20it.%0A-%20Show%20the%20item%20list%20on%20cards%3A%20yes.%0A-%20Customer-facing%20variant%20I%20will%20run%20on%20a%20second%20screen%3A%20%2Fboard%2F%3CBOARD_TOKEN%3E%3Fcolumns%3DReady.%0A%0ADEFINITION%20OF%20DONE%0A%0A-%20%5B%20%5D%20A%20signed%20order.created.v2%20creates%20a%20card%20in%20New%20within%205%20seconds%2C%20showing%20the%20code%2C%20items%20and%20a%20running%20timer.%0A-%20%5B%20%5D%20order.preparing%2C%20order.ready_for_pickup%2C%20order.completed%20for%20the%20same%20id%20move%20the%20card%20one%20column%20at%20a%20time%2C%20and%20it%20disappears%20DONE_MINUTES%20after%20completed.%0A-%20%5B%20%5D%20Receiving%20order.preparing%20after%20order.ready_for_pickup%20leaves%20the%20card%20in%20Ready%20\(it%20never%20goes%20backwards\).%0A-%20%5B%20%5D%20The%20same%20event%20delivered%20twice%20changes%20nothing%20the%20second%20time.%0A-%20%5B%20%5D%20A%20bad%20signature%20returns%20400%20and%20the%20board%20does%20not%20change.%0A-%20%5B%20%5D%20order.canceled%20removes%20the%20card%20immediately.%0A-%20%5B%20%5D%20%2Fboard%2F%3CBOARD_TOKEN%3E%3Fcolumns%3DReady%20shows%20only%20Ready%20cards%2C%20large%2C%20with%20no%20item%20list.%0A-%20%5B%20%5D%20%2Fboard%20and%20%2Fapi%2Fboard%20with%20a%20missing%20or%20wrong%20token%20return%20404.%0A-%20%5B%20%5D%20Neither%20the%20HTML%20nor%20the%20JSON%20contains%20an%20email%20address%2C%20phone%20number%2C%20surname%20or%20delivery%20address.%0A-%20%5B%20%5D%20Turning%20the%20backend%20off%20and%20on%20loses%20no%20cards.%0A%0AWHAT%20TO%20GIVE%20ME%20WHEN%20YOU%20ARE%20DONE%0A%0A1.%20The%20public%20webhook%20URL%20\(%E2%80%A6%2Fwebhooks%2Fstorekit\)%20and%20the%20full%20%2Fboard%20URL%20including%20the%20token.%0A2.%20The%20environment%20variables%20to%20set%20\(STOREKIT_WEBHOOK_SECRET%2C%20BOARD_TOKEN%2C%20TIMEZONE%2C%20optionally%20DONE_MINUTES%2C%20WARN_MINUTES%2C%20LATE_MINUTES\)%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%20open%20the%20board%20in%20full-screen%20mode%20on%20a%20TV%20browser.%0A)

[![Open in Replit](https://replit.com/badge?caption=Open%20in%20Replit)](https://replit.com/?stack=Build\&prompt=JIAg7ghgdgLiEgDYEsBuBTEB7ATgE3RxACMsJ8QAzXEAWwE8Qd0BnGCAVx2hgC54qHRIgC0LAMbN0UcOmIgADhADmmGAAsIcFuqxgW2BdOz5CBiAfHk8B5DPFZEHWlHNQ8dLBgMb0teIhYUMrwBmy46ADWyHAw6MK2cBCkHNrsMByWmsGsAHQgAHJYpHiMdnHc4jAGYDHqIL4kZPgA3CAxIABWmWkQ9Obi4lxa6PnhzNHa0jYN6phsWpkg4tmq5jVyuliRLLkAUHsAQhzIiB4dtRoCxBDikdOzWiCa5oocxCjiIAASACq-AAUAMogACqACUADJUGiNMCbLDbEAACgICkCZSS7ga8UQdDUczBUIAlAAaAQsWgQYQgPBaZIWTDUIiNIY4ZiwEwEHAscnQDwISg4IJwB4EFjIZRQdAeZkCX4ANX2ewAwgB5Aq-ACiAA1fiAAIKHNWg-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-aPIus-c7YRIiA3-NEVLVQhAKwkoZ2Yqlxbfme0qjmAy1HrHNm7f4BHSDxFjuwFO4Tli6IOD-cp3dh7NOH0QHe5925Vs+I-bdT13i963Vtcqv58dh2p2PcJ8ui2W3ofLYog7Qa21ODcFgBVlgThlA9oYCIYs7BSxCYl0YvlAY7OA8nWwKQMBQ49ryyAYbc44XzeIibpKMA90UTt3x8nxFIQ9yCK752bA6uboosLUOBoQDB4NK7+ZsB1o9tBNaCPkboD0Ax9LxgQ0SwK7gJCGAj2XsOBUnYEYRn+BQ7zJ2DIAeQDh1Drke5RXxfEUCMEGIHAK+4dyIu0r7PAfc7Xa13j8nuW8v5YboXDvKHm4ooVmBEAV4GgwErqhjvnfEUmaXkAe6Jdgzoj2z37hvcS99zAf3sOtShxAAVg0BQKuR6oYhiiseKuGZpz1vjiJ8AF7iEXziDejbl-81XmvCOcOFiRvSCP-DiNvK2IrayBtHvCdOA7nbnPYQRQQGkFANeNxZOOiIwAVMlZtGYB8eAKcXVBQWScKcKeCGAJLcKMucQFgGg0iQIIwHkcKeEdcREHYVgw8SIEQPKA4X4b4LUOOX4A0X4UEEELUBULUTUIEA4Q4VgS6A6PtfjAdaya6eFFsDOGOHCHRPKFGXRWNUyExDoDhbwWYYUDgUbQ6SYJHKAfIPtSmSSdmYIckPtbxegQWZkQWFDKFKKdGGATw3AQWAfUFKKaMCtTJOMDqKtKMaACSJ+AIPETuAkQgykWRfZPofA8kQGNOXIPtSJMIShFORoaUMAQOc+VtQXEiZqGdGIxwpQdFVXADViekEfaDKtMdYiadCiWdedPLXHDdWHbdXdXHAonteo5w1XPiPnaQKqTo4XPbMXFiGHFbVPOXbXUYlXNXegDXdYjPIiLvdnJAoEBeStYxS6LVXIi4mxQkIKCmTIsgAUbEXIvta42EQkNInmbNaUWVcbFyJMIKVuaSBVJ4EmLgVwQ6C49MQYquMNemOoNyHQVVLRX4Q1VseI8hAwIiZgUwpkauJoawA41+VIQInAYIj9ZGIBIiQLDLQSMbX5deYCW4CSKSFKGgZgFWSIrSRxeMKtQOfoNhOhMohZU4cJAwRzNeAojxeAPabwZUVabMSwOmcgTZROfw7ALqfyRJGAWoCSfIOFCIAQBXFeZg8opYZEFQlKYnCYxo9fXOdqDwrwnwocJdVwkkrwkIy2YqSNBSSI+vcRDEjwH9F+bEaUCpXEgQXaDwJsAQQIeEIgY0x4GQDoakdwwBXYA4YWb4UQkAC0EAQ4UEYASEAFA4avV8d8fULg9go8CgjQjoQAFAIQB9Y-pGgFolopopRPpMBAY-JKAQYwYIYYZ4ZEZyAuFsQ+YrRbRfgRYxYJYpYZY5YFZp5Z415GhuAyjNoxo4BMI2hrY2cgxH4uAtF45E5Ghk4KEMgcAZBC42cxUsx6kRg5FjIFA2ICQnJmgPAz4f4KhIB8BY5gBAlJpE9To3ooSvEHTgw-F9hrJ8gYk2VKVOZLF0B6AUpiAFw6y4AtSLi5p+BBj1Cg5c5oBIhXCq1vkjBi4qVfVDDkQaBuNYxrMyoqAs414YdVkOsL5WKQBScy9QjgcDY3IYdyxDNYwtTDNipDUZRBNRksV1pFMKpZiqKsxyQfN9IXzLJczbibUwiJKQobJG18CmKEgml0AhRWA5gDB6Ful4g8A2hPi8Q8DHiDBsFXzsAoA6UCjCKoAV50wVKsE1K6124upT4qR7im1HiZTVSDo5Q6YtTGhkIKBsIwzESko55lgJLIzrBj56huhVzCQYSXp0glgWBgIOhCDR5t4qC9gl18gABxLUSs+KvAcKSiY0A0cEAFQWC0a0aQhiEARsxoO40QCQKQGQeJbZXNQgTAVq9qzqqWaQrVBAUApgfkOEjOGtOAIUOEumBmLOIIFmOANmTZSiukyarVdsJALASCGQTeKwEyDoJ0CE5BYMGtYwTePBaMvKkDCofVRoJse4ZMhVTAUCLVPyNyxgDWM1JKD0fBBHHIHkbAVIHkjaEaLclOcKRqxJcKVVZAdGj8rK9Us8stVWP64wVQHMa2C2KIxkiGkIeIDOHyD6tQbYYwQpFisJTNCyIgOcQIZQQMIgQgYUIgMa2OeQuM9ucAP4iKDGuUHQXAGANoakSAAUuwcQJwXqX65mmQDOZQfak6jMBJVmuAUeFWtWrmUNRi+KK82NdBdLbZMLUUmgT0vXflNWCa45QG-WytMUvqCbTc7aWOOrJwFwcsBSX+FOTZUbPgQodAMoi0nS2MAEZxSY8kIaIxRgZEdw0k7wiFbBWMGkzAC0n0yhGUN0gIj0ik+gYkfIXFP84MXQMAOSQkLUmE6VPMNbbZGDIwKm9gRCzi1kc2wgAAcnFOYqNutshI63hjXipIbQAHWrcRsxtPRJsZs5ssqdl+L4a7ACBYByFQz0lbBkyBlQZyx0wUExVET67VwLlrVP8ppNE0ra1JKqCQAa6ZhyAzjJqOozhtomLs5upFD6ZrxlhHBnA7DX7rBD6QB87aQHwObyBoGNQtRBY7ZgAChTQtQQRuE3JfBHZd66loHv4UQCAsohA4BiMq6IHa65QAz4g2Sq0DAgN4GubaB7zwY4g5F9gLY9axqPZTKdADoYhGH6RD5kZI41qe5g4NB1TKBPh7giBkRKBKED5fx-xNr-A6rKzsbcbrBmrpqOquqeqqbmBdj3A0FEkTIM5SlCAxAj48pKHgAgq+Hix6hH5yx7hJI669BNZCRfK04qbZayjgrZFiJmBo5pQqhUwDi8wLM2gkqiBAnrg8wvKCT8B9hw5BIL6iAHBAguBZJaQTLOBEB9Iyc8RhY2qCgUG0GMGQRURCmyHQ8rZYxhF00sFSmQBIRRDkHUH0HtRamSGim4BC5YwCHsGzIQBJkaB-IpH6gEAQjFM2EBrUgjzvSFlaQ9BYJgJ5mL7uo4gjEqaYn4hwliI5RQ4tMbYiJ9gCt8ggUJQpR9CEBiMSsrYhxFQmz9p4RZR8SNApB8RF98m6QAEbg7hlALD3ByQb6xVnB18SsFAA5MIcQGoSLjFBjzA4AFILA4Af0Cs4XyRzrdA1oRBWE7ACwjYgg8XgJhrHBEB6wDBGyuxIzQGXBylCBZG9BywdB0UV4oyDBQXbgTKbUxzPz67iJMsChPBmAiTRx3zdUw19git8hJlTgKg9ZwYuKqpkYTdVxirxbxFFAXZORdklGORtAXqISngjXJrWYPn8mAB+bA1gAAXgJUhRGXg2lUCdpXBrdE-pwDItYDaFtZybAZYEddTp8Xrq9ceB2iZZkEBkKQPmIElFUGycgYeZDVKhsayjBkJmxIgpjJyYUiqAOOGpSsDf50dcomV0sN6oZcaGZK+vTIlYJBiqTH532BK1qvqoikMWKfqC1jgDBKvIMBvKpqh22BPSaz4ipr0jwVpsQAzhWmlQ7DGyYH8MwENp9XhN8CIB0fwD0bVDaoMbmoKAYnJCxo9D3aapasPZmsMdPe1MXm4OVDtgAE18zb2SyKVFMptv5+BfhgA7YtRKMkGklGY9qj5DqOYFJbKRWQnUEoGaEKUgQRXGgnUp7ML7BIH+AUKMyLxEVM2cARBs3VwoOeBxbyY8QHrXKiZo5PyzX+Ar2D2j3ZrurT2g3Y3Q3w3yU9gAUtRJk0G7RgB-w1RJlEGCgtQDgLwXUQA3VDQOyZptKn7dKG1LTXgozBSY6sq5xxGvJJHror7CZWQsACA2K-ABUnFnQoBNYsmBoZO5OHCk7GjkXCBooHSs7nSXPq0i7IibihERELiIzWRMqiHg3mWnhz6lKqaEI4GZxyADAAUkGqnenMH4Bf43JwjfSZQ7PZPZcJI0BVxHOnDbSxm0p3OnSc6hwMWzDguKA5xuOUQOh4mQBlAgsDBgXIhfybAq6KV7ONL-PMBk4B8UodStllhVgDpZ2fH5g9PfIL6cu5OQ8bhPyPpLzMAh2IS9zu6MI8ay5JuOxxusZRhevcvlC4i6GPAcSvADpavzhWGZR2H4hyVpPcumOb2WP72GIOOg7XAw2ILPXXK6UGueXlKRtxqvxzr0P0DKqXu5Or2qaL2caMafZjVclCuiAwAmYQgSaZANvLYrZuGTu5O6EESWRCQ-g7ZoR2wyfMBVHxXo52BWp3F7a8RnaAwbaiHrUONzHLaYdEknah8AwFvczwTpuSBbh-qOo+yqagxt59uKw-zlQsycy8yargApCrchCsz5rX2TRDQrRxPJO9gyyUCCoPhQY1wNwiRoRkRAAyAirI3HoMoJDMZtQLxHh+BpNpbzF5x+gr1p2qZn2qbPZkop8EpaKORAnIFmnKYznOlllnlnqvJH0dY+kPJAA6A5A4k9cKkmkVkUYES4k+S5qfJHKfBEqZ6ZL46a6eL76Zd5CVOpjIzmlf8CDA1nylLwLH2GqtfqCEcypGBOjcOg64eAWAFKJWw2eHRnhrmb6Cpr24MFHmVyhp9R1JSo9i2nGi4fyG3DKJjKwCvmH7xrnEGrEDNZ2X+PlAVBIGFH0Fc72CAA\&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.

> **Prompt complet (cliquez pour développer)**
>
> ```text theme={null}
> I want a live order board for my restaurant: a full-screen web page that shows open orders as cards in columns and moves them along as storekit tells it about status changes. Nobody interacts with the board; it just stays accurate. storekit sends the status changes as webhooks.
>
> 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 the current orders, and a front end designed for a TV.
>
> ```
>
> ```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 STATUS EVENTS
>
> Besides order.created.v2, storekit sends these events for the same order as it moves through the kitchen. order.preparing, order.ready_for_pickup, order.out_for_delivery, order.completed and order.canceled all have this smaller payload, with data.order.status set to the new status:
>
> {
>   "event": "order.preparing",
>   "data": {
>     "order": { "id": "ord_abc123", "code": "A1B2", "status": "preparing" },
>     "venue": { "id": 1234, "name": "My Restaurant", "slug": "my-restaurant" }
>   }
> }
>
> So the order id is data.id for the full payload and data.order.id for the smaller one. Write one function that returns the id and code from either shape. Treat canceled as "remove from board" and out_for_delivery as "Done". There is no accepted or rejected event: an order stays in New until its first status event arrives.
>
> Events can arrive out of order or twice. Store a rank per status (created 0, preparing 2, ready_for_pickup 3, out_for_delivery 4, completed 4, canceled 9) and never move a card to a lower rank than it already has.
>
> 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; update the board state afterwards. Ignore any svix-id already saved.
> 2. An orders table keyed by storekit order id with: code, status, rank, orderType, table name (or null), customer first name, items (name, quantity, modifiers names, notes), order notes, createdAt, deliveryTime, venue name, last updated. The full order.created.v2 payload fills or refreshes every field; small payloads update only status, rank and last updated. If a small payload arrives for an order the board has never seen, create a card with just the code and status so it is not lost.
> 3. GET /board/<BOARD_TOKEN> — the full-screen page, where BOARD_TOKEN is a long random secret from an environment variable. There is no login because it runs on a screen behind the counter, so the token in the URL is the only thing keeping strangers out: any request to /board or /api/board without the exact token gets 404 and nothing else, and the token must never appear in logs or error pages. Below I write /board for short; always include the token segment. The page must not include customer surname, phone, email or delivery address anywhere in the page or its data requests. Columns, left to right: New (created), Preparing, Ready (ready_for_pickup), Done (completed, out_for_delivery). Cards show: the order code very large, orderType and table name, the customer's first name, the item list as "2 × Margherita Pizza" with modifiers indented and notes in italics, and a timer showing minutes since createdAt. Cards are ordered oldest first inside each column. Cards in Done disappear DONE_MINUTES after they entered Done (default 10). Cards for canceled orders disappear immediately.
> 4. The page refreshes its data every 5 seconds without flicker (fetch JSON from GET /api/board/<BOARD_TOKEN> and re-render, or use server-sent events). If a refresh fails, keep showing the last data and show a small "reconnecting" label; never show a blank board.
> 5. Timer colours: default until WARN_MINUTES (default 10), amber until LATE_MINUTES (default 20), red after. For orders with a deliveryTime in the future, count down to deliveryTime instead and label it "for 11:00".
> 6. Design for a 1080p TV viewed from three metres: dark background, minimum 28px body text, order codes at least 96px, no hover-only information, no scrollbars — if a column overflows, shrink cards gracefully and show "+N more" at the bottom.
> 7. Filters via query string so I can point different screens at different views: ?types=Pickup,Delivery shows only those orderTypes; ?columns=Ready shows only that column with much bigger cards for a customer-facing "ready to collect" screen; ?venue=<slug> if the account has more than one venue.
> 8. GET /health that returns 200 and { "ok": true } and nothing else. Every other route must be either /board/<BOARD_TOKEN>, /api/board/<BOARD_TOKEN> or the webhook.
>
> MY BOARD
>
> - Timezone: TIMEZONE environment variable; show all times in it.
> - Show the item list on cards: yes.
> - Customer-facing variant I will run on a second screen: /board/<BOARD_TOKEN>?columns=Ready.
>
> DEFINITION OF DONE
>
> - [ ] A signed order.created.v2 creates a card in New within 5 seconds, showing the code, items and a running timer.
> - [ ] order.preparing, order.ready_for_pickup, order.completed for the same id move the card one column at a time, and it disappears DONE_MINUTES after completed.
> - [ ] Receiving order.preparing after order.ready_for_pickup leaves the card in Ready (it never goes backwards).
> - [ ] The same event delivered twice changes nothing the second time.
> - [ ] A bad signature returns 400 and the board does not change.
> - [ ] order.canceled removes the card immediately.
> - [ ] /board/<BOARD_TOKEN>?columns=Ready shows only Ready cards, large, with no item list.
> - [ ] /board and /api/board with a missing or wrong token return 404.
> - [ ] Neither the HTML nor the JSON contains an email address, phone number, surname or delivery address.
> - [ ] Turning the backend off and on loses no cards.
>
> WHAT TO GIVE ME WHEN YOU ARE DONE
>
> 1. The public webhook URL (…/webhooks/storekit) and the full /board URL including the token.
> 2. The environment variables to set (STOREKIT_WEBHOOK_SECRET, BOARD_TOKEN, TIMEZONE, optionally DONE_MINUTES, WARN_MINUTES, LATE_MINUTES) 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 open the board in full-screen mode on a TV browser.
> ```

## Étape 2 : connecter 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.

Cochez `order.created.v2`, `order.preparing`, `order.ready_for_pickup`, `order.out_for_delivery`, `order.completed` et `order.canceled` pour cet endpoint.

## Étape 3 : tester avec une vraie commande

Ouvrez l'URL du tableau (avec son token) sur l'écran, puis passez une commande sur votre propre boutique. La carte apparaît dans Nouvelle ; faites avancer la commande à travers ses statuts dans le tableau de bord (voir [Gérer les commandes en direct](/docs/fr/guides/orders/managing-live-orders)) et regardez la carte suivre. Annulez une commande de test pour la voir disparaître.

L'acceptation d'une commande n'envoie pas de webhook, donc la carte reste dans Nouvelle jusqu'à ce que la cuisine la marque en préparation ou prête.

## Idées

* **Sons** : un carillon lorsqu'une carte entre dans Nouvelle, un autre lorsqu'elle entre dans Prête.
* **Tableaux par poste** : marquez les articles par catégorie (le builder peut faire correspondre par nom d'article ou par `plu`) et affichez un tableau par poste, comme le fait le [ticket de cuisine personnalisé](/docs/fr/guides/build/recipes/custom-kitchen-ticket) sur papier.
* **Alerte commande en retard** : quand une carte vire au rouge, postez sur Slack en utilisant le pattern d'[Alertes de commande](/docs/fr/guides/build/recipes/order-alerts).

## Ce que cela ne fait pas

* **Cela ne peut pas modifier une commande.** Le tableau est en lecture seule ; accepter, marquer prête et terminer se fait toujours dans le tableau de bord ou sur votre caisse, ce à quoi sert le [Kitchen Display](/docs/fr/guides/orders/kitchen-display) intégré.
* **Cela ne sait que ce que disent les événements.** Il n'y a pas d'événement `order.accepted`, donc une carte reste dans Nouvelle jusqu'à ce que la cuisine la marque en préparation ou prête. Une commande refusée n'envoie pas non plus d'événement ; elle ne bouge simplement jamais. Si une carte ne bouge pas, consultez le journal de livraison des webhooks dans le portail pour voir ce qui a été envoyé.
* **L'URL est la clé.** Toute personne disposant de l'URL complète du tableau peut voir en direct les codes de commande, les prénoms et les articles, alors traitez-la comme un mot de passe : ne la partagez pas hors de l'équipe, et changez BOARD\_TOKEN si un écran est perdu.
* **Cela démarre à vide.** Les commandes passées avant que l'endpoint ne soit connecté n'apparaîtront pas.
* **C'est votre service.** Le support storekit peut confirmer que les événements ont atteint votre URL et le code de réponse ; le tableau est le vôtre.

## Voir aussi

> **[Événements de commande](/docs/fr/developers/webhooks/webhook-events#order-events)**
>
> Tous les événements du cycle de vie d'une commande et leurs payloads.

> **[Kitchen Display](/docs/fr/guides/orders/kitchen-display)**
>
> L'écran de cuisine intégré et validable par bump de storekit.

> **[Statuts de commande](/docs/fr/guides/orders/order-statuses)**
>
> Ce que signifie chaque statut et comment les commandes passent de l'un à l'autre.

> **[Écrire un prompt qui fonctionne](/docs/fr/guides/build/prompting-guide)**
>
> La structure derrière ce prompt, et quoi dire quand le builder se trompe.


## Related topics

- [Intégration de livraison en marque blanche Uber Direct](/docs/fr/guides/integrations/delivery/uber-direct.md)
- [Gestion des commandes en direct](/docs/fr/guides/orders/managing-live-orders.md)
- [Statuts des commandes](/docs/fr/guides/orders/order-statuses.md)
- [Créer des stories menu avec du contenu vidéo et des CTA](/docs/fr/guides/marketing/stories.md)
- [Aperçu des commandes](/docs/fr/guides/orders/overview.md)


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