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

# Een prompt schrijven die werkt

> De vijfdelige promptstructuur van de storekit-recepten en het contextblok dat Lovable, Bolt, Replit of v0 vertelt hoe webhooks binnenkomen.

AI-appbuilders doen wat je ze vraagt, en raden de rest. Een vage prompt levert een mooi scherm dat nooit een bestelling ontvangt. De prompts in onze recepten volgen één structuur, zodat het gokwerk al gedaan is voordat je iets plakt. Gebruik dezelfde structuur voor je eigen ideeën.

## De structuur

Elke prompt heeft vijf onderdelen, in deze volgorde:

1. **De uitkomst**, in één alinea, zoals je die aan een nieuwe manager zou uitleggen. Wie het gebruikt, waar het draait, wat het nooit mag doen.
2. **Het storekit-contextblok** hieronder — volledig gekopieerd. Het vertelt de builder precies hoe storekit met de app zal praten, zodat er geen payload verzonnen wordt of signatuurcontroles overgeslagen worden.
3. **Wat te bouwen**, als genummerde lijst. Benoem het endpoint-pad, elk scherm, elke regel. Als je iets bij het testen zou controleren, hoort het in deze lijst.
4. **Definitie van klaar**, als checkboxen. De builder gebruikt dit om zijn eigen werk te controleren.
5. **Wat terug te geven**: de publieke webhook-URL, de lijst environment variables die je moet instellen, en hoe je het test.

Houd het hele ding in één bericht. Vervolgberichten zijn bedoeld om te corrigeren wat de eerste verkeerd deed, niet om toe te voegen wat je vergeten was.

## Het contextblok

Plak dit ongewijzigd in elke prompt, direct na je uitkomst-alinea. Het is hetzelfde blok dat in alle recepten wordt gebruikt.

```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
```

Als je tool een ander event dan `order.created.v2` gebruikt, kopieer je de voorbeeldpayload van dat event uit de [eventsreferentie](/docs/nl/developers/webhooks/webhook-events) en voeg je die ook toe onder "Example payload".

## Regels die vaak herhaling behoeven

Builders vergeten deze zaken tenzij de prompt het expliciet maakt, dus elk recept schrijft ze uit. Kopieer de regels die je nodig hebt in deel 3 van je prompt.

* Verifieer de Svix-signatuur bij elk verzoek met de officiële `svix`-bibliotheek. Schrijf geen eigen verificatie. Retourneer 400 als het mislukt.
* Retourneer 200 zodra het event opgeslagen is, en verwerk het daarna. Laat storekit nooit wachten op een printer, een sms of een externe API.
* Negeer elke `svix-id` die je al hebt gezien.
* Bewaar de eventgeschiedenis in een echte database, niet in het geheugen, zodat er niets verloren gaat wanneer de app herstart of opnieuw wordt gedeployed.
* Stuur klantnamen, telefoonnummers of adressen niet naar externe services tenzij de prompt dat aangeeft.
* Formatteer geldbedragen door te delen door 100 en de valuta van de vestiging te gebruiken.
* Toon tijden in de lokale tijdzone van de vestiging, niet die van de server.
* Zet de webhook-secret en API-sleutels in environment variables. Nooit in de code.

## Een builder kiezen

We raden niet voor alles één builder aan, maar de recepten geven aan waar het uitmaakt:

* **Tools die webhooks ontvangen** hebben een stabiele publieke URL nodig die online blijft en meestal een database. [Replit](https://replit.com) biedt alle drie op één plek, dus recepten die events ontvangen zijn met Replit in gedachten geschreven. Bolt en Lovable kunnen dit ook als je een backend toevoegt (meestal in combinatie met Supabase); voeg bij het prompten toe: "deploy de backend zodat die een publieke URL heeft, en vertel me welke".
* **Tools die alleen een scherm tonen** en uit een database lezen die je andere tool vult, passen goed bij [Lovable](https://lovable.dev) en [v0](https://v0.dev).
* **Geplande taken** (een dagelijkse samenvatting om 23:30, een wekelijkse digest) hebben ergens een scheduler nodig. Vraag de builder direct: "dit moet elke dag om 23:30 Europe/London draaien; vertel me hoe je het hebt ingepland en hoe ik kan controleren dat het draaide".

Welke je ook kiest, raadpleeg de eigen documentatie van de builder voor hoe hij secrets opslaat en de gedeployde URL toont — de namen veranderen vaker dan deze pagina.

## De voltooide app aansluiten

Zodra de builder je een URL geeft, sluit je die aan:

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

## Testen zonder op een echte bestelling te wachten

* De testbezorgoptie in de webhooks-portal gebruikt een generiek voorbeeld, niet je menu. Het bewijst de verbinding en de signatuurcontrole; het bewijst je layout niet.
* Voor een echte payload plaats je een kleine bestelling op je eigen storefront met een testbetaalmethode of een 100%-kortingscode, en refund je die daarna. Doe dit met het log van de app open.
* Breek expres één keer iets: verander `STOREKIT_WEBHOOK_SECRET` naar een verkeerde waarde en bevestig dat de app 400 retourneert en dat de portal de mislukte bezorging toont. Zet daarna de juiste waarde terug.

## Als de builder het verkeerd doet

Plak de foutmelding, niet een beschrijving ervan. De meeste fixes zijn een van deze:

* "Er staat 400 in het storekit-bezorglog." De secret in de app komt niet overeen met de secret van het endpoint, of de app parsed de body voordat hij die verifieert. Zeg tegen de builder: "verifieer de ruwe request body, exact zoals ontvangen, voordat je JSON parsed".
* "Er is twee keer geprint / twee keer sms'd." De app controleert `svix-id` niet, of doet het werk voordat hij 200 retourneert waardoor het een time-out krijgt. Herhaal beide regels tegen de builder.
* "Het werkte, en stopte na een dag." De app is in slaap gevallen of de geschiedenis stond in het geheugen. Vraag om een always-on deployment en een database.
* "Prijzen zijn 100 keer te groot." Minor units — herinner het aan delen door 100.


## Related topics

- [Custom keukenbon op een Star cloud printer](/docs/nl/guides/build/recipes/custom-kitchen-ticket.md)
- [Live-orderbord voor de pass](/docs/nl/guides/build/recipes/live-order-board.md)
- [Reconciliatiesheet voor uitbetalingen](/docs/nl/guides/build/recipes/payout-reconciliation.md)
- [Uitverkocht-log en ochtenddigest](/docs/nl/guides/build/recipes/out-of-stock-log.md)
- [Ordermeldingen naar Slack of je telefoon](/docs/nl/guides/build/recipes/order-alerts.md)


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