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

# Reconciliatiesheet voor uitbetalingen

> Volg elke storekit-uitbetaling in een grootboek gesleuteld op svix-id, met vinkje voor gereconcilieerd en CSV-export, gevoed door payments.payout.created.

Elke uitbetaling die storekit naar je bank stuurt wordt een rij in je eigen grootboek zodra deze wordt aangemaakt: bedrag, valuta, welke bankrekening en wanneer je hem kunt verwachten. Vink elke rij af zodra deze op je bankafschrift verschijnt, en download de maand als CSV voor je boekhouder.

> [!TIP]
>
> Het dashboard toont uitbetalingen onder **Analytics** → **Accounting** → **Payouts** (zie [Hoe uitbetalingen werken](/docs/nl/payouts/overview)). Bouw deze recipe wanneer je je eigen reconciliatiestatus wilt (afgevinkt, notities, verwacht-vs-aangekomen), een export die is toegesneden op je boekhoudsoftware, of een Slack-nudge wanneer een uitbetaling verwacht wordt.

## Hoe het werkt

1. storekit stuurt `payments.payout.created` naar je app wanneer een uitbetaling wordt aangemaakt.
2. Je app voegt een rij per uitbetaling toe met het totaal per valuta, de laatste vier cijfers van de bankrekening en de geschatte aankomstdatum.
3. Je vinkt rijen af tegen je bankafschrift; alles dat voorbij de aankomstdatum is en nog niet is afgevinkt wordt uitgelicht.

## Wat je nodig hebt

* Webhooks ingeschakeld op je storekit-abonnement (**Settings** → **Developers**).
* Een [Replit](https://replit.com)-account — het grootboek heeft een database en een URL nodig die blijft draaien.
* Optioneel: een Slack-incoming-webhook-URL voor een wekelijkse "nog niet gereconcilieerd"-herinnering.

## Stap 1: Bouw de app

Plak het hele blok in de builder in één bericht.

[![Open in Replit](https://replit.com/badge?caption=Open%20in%20Replit)](https://replit.com/?stack=Build\&prompt=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-VCHtaurlZpSS4S02QHJeC8DBEuJ0TziRYCaD-RYuDlEYLuWCvFcZki0ykDKL4pg471wLHgp8rBkEamWTAZSnc6AOS4MssJLYhWzHTrQy+UAFAYGSCJSa0YhnIEQAAbQALqJA1QwmUMSQAqqRjAgIDEc6jXiiIRKxoHUyDcfSeYaVoRWHtY6ogMB6QKGQDE4QBZijCB3uATlSB37unKV+C4TIaLzHdIsOSClYZ8USGpcosg0AiAKhOM44EzggGgihAiUFLLpuUpm1S2Fi3QQqgRItJaS1QVKuW6Y1N8hWAdqpAAzHWlt0FbDyXLRyB2lxa32Hra2t4bQCIwClBsGd0EanoAIpsqOK6oIZiwBurZ06h24WZW0tgG6ACc57D0ttwl0uwvT+loAIpXbdpzznlpBUSwdN7-7MGpbShY5bI6qSVoiFWDtETOyaA7FWY4lZOzHGrAAWt+1tpRoDdCfQhKC9NLioegrrf1gahAEQtXU7dVa+AchgEBlWIGwMQagzBscfbYPIfw8e0FSCAMkFo-R8DkGnbQYdohtjKsUPXtbRqxA5bSQtiEuoGJGAONeq1IlJtK7W3B1UG6wD2HdwiHoFAfDraPQ6fdR+kQvHJM3oCGFctBlDNQAAAK0STTwJ1yATMEbqN0ctABqJWSte19vgwANgAOwAA5z3eagqgROcN7TUiYgkBdS6V2kRs220gOqCK6s04RQrUmLPYY1rGfgUh8ABjXHgAAXnViA3nW3TNgLp3jZWKtVcVM1gj+B1gEV7VuodR76rBmVINmzR73QKFI6q8jI2f0DGALwObGAps3oDDRUkcQN0vsW1JgJa2FsHfixMu6s18vFZbZBKCbC9PQQTYuhApQ2C6Gwn1-W7Xy3UikCCazn2BsIWdpXaCY3YATeBzhGbx3ofLdWwhMjOEts7dIHtsi139WFdIiu-V2WUodaK4tttCOQAhaVhtu7pXoJa2qv6QM304KU8QGgBQ-By0mERGCAMQYmeFaggVMl6oNMHegqoDQcMHblpCyAcrNYPm9aguLtgMBa3YcVz4GAshy3klNksRXutVApdHdh2mDsORk45ByDX96kHluJMKG3G1oCyG+2V0wNVYi87gOSGAq2NtofqHJUIcQZMIVux6DMc8TcgCdg7RIWyIsU+grm4G2gFAx7A4kXt0WMeLZx0OrL27CUDRJULsJIuRtK8XSrqX2HwsgFpBASwHJehxeV3DNX0FWmR-LnFzX2vsO6-0PrynhuYDG8J1BJkDsQBhY5PSfvtvB-QQd3F6TsP+e2GmCH0gYfCIp5ltHwnceE-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-VGG6B6DWPSQqXbI8DAFRPkusIAa2V-XfLIIGo-U4EeA0KwOwC2B8MfJAHMtCRCMBIEFYDwNALVS4I+dmpUHWaXX3YwJaMsFQzYtVvSmQPvYfd7VPU8ckVSE7M-AxKwo9DUJoHghoCSW5AhJmOTOSByNSKzLhhFazBeNRboKHOgqYBrKbDdUwCrOeqJrdfdY9c9dgMPNBAhercWbUtaA6CyPWeqXDBA7A1A6IJUiNCiYEIhB-VhFfcICJC3VDMY18I1fWGmN0B7FoLQAkJkhUsghIE+ATA9FHXeGSXY1SGFizWzc5NNEbWUCLHABoCVa7eVdGLdg0OlqlGRInTNS2BUroOpdckyK0hLWKMAIeLSK0iANcs9RcFOLqiAPqrQTkKIcmkAbvW7ZIaNA5JNNfd7XbasFDBTiBmKVlWkVuI8MA-AmA5gJxSCXRhcVVOs5s9QoqmQAJCXoieZKsAnZRvGTfRWViV85yDGDMPtGwiVTnXDACK7ZlUVVMHJBrSM+vZCxs6gliw6I+DkAHG8GMh4imv0ARaNHOXFbMLBS4PMQhbxX+XACqsRsIPqeGlBNsdFsxVrn4IDYiPucIEGZ87i7SgAi2H7dE+FCNeYpVRHTVQFbCwCECL0Nabnfs9GFovThCKK98zkAo6IIvOYl-PKEYm+CrvczuNgzJay0GoRSAFCxbo2UdSdeMmXRI1wDo39fwLXdCPXPPWTDUy3ZVu3RAU-BcJpdpbQbSCAH4cAFQbLhQZpdQSAAAJo8ichChLMrNnAmXUJYBogYhWADi4EkgUh6iABkBBZYOCgXAdoF+Y3N8HwC6eW5SBtc0Ns1ANjEsITZUyrYYM4HY3qEFVTKFZY8zKzOzJzGzY4KLeLZLdLbLfLaSNco4HdQ9U9fSEyFfZo0sLGQSKY+Y2FYzJO9TBeI2xGoicWY2BkFIIECiOPfkjAL6MgEzckCllAMHCRawgIne2lO0LENXUAiBoBMDdac8DnGcEAA\&referrer=docs.storekit.com)

[![](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%20private%20web%20app%20that%20keeps%20a%20ledger%20of%20the%20payouts%20storekit%20sends%20to%20my%20bank%20account%2C%20so%20I%20can%20reconcile%20them%20against%20my%20bank%20statement.%20storekit%20tells%20the%20app%20about%20each%20payout%20by%20webhook.%20The%20app%20must%20never%20lose%20a%20payout%2C%20never%20record%20the%20same%20one%20twice%2C%20and%20never%20make%20storekit%20wait.%0A%0ABuild%20it%20with%20a%20database%20so%20the%20ledger%20survives%20restarts%20and%20an%20always-on%20public%20HTTPS%20URL.%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%20EVENT%20THIS%20APP%20USES%3A%20payments.payout.created%0A%0AIgnore%20the%20order%20example%20above%3B%20this%20app%20only%20cares%20about%20payments.payout.created%2C%20which%20looks%20like%20this%3A%0A%0A%7B%0A%20%20%22event%22%3A%20%22payments.payout.created%22%2C%0A%20%20%22data%22%3A%20%7B%0A%20%20%20%20%22amounts%22%3A%20%5B%0A%20%20%20%20%20%20%7B%20%22currency%22%3A%20%22GBP%22%2C%20%22value%22%3A%20%223210.50%22%20%7D%2C%0A%20%20%20%20%20%20%7B%20%22currency%22%3A%20%22GBP%22%2C%20%22value%22%3A%20%221030.00%22%20%7D%0A%20%20%20%20%5D%2C%0A%20%20%20%20%22bankAccount%22%3A%20%7B%0A%20%20%20%20%20%20%22id%22%3A%20%22db97e205-105c-42ff-8460-d06c25cb6830%22%2C%0A%20%20%20%20%20%20%22IBAN%22%3A%20%22GB15HBUK40127612345678%22%2C%0A%20%20%20%20%20%20%22accountNumber%22%3A%20%220001234%22%2C%0A%20%20%20%20%20%20%22branchCode%22%3A%20%22001234%22%2C%0A%20%20%20%20%20%20%22currency%22%3A%20%22GBP%22%0A%20%20%20%20%7D%2C%0A%20%20%20%20%22estimatedArrivalDate%22%3A%20%221%2F2024%22%0A%20%20%7D%0A%7D%0A%0AImportant%20differences%20from%20the%20order%20events%3A%0A-%20Money%20here%20is%20NOT%20in%20minor%20units.%20Each%20value%20is%20a%20decimal%20string%20in%20major%20units%3A%20%223210.50%22%20means%203%2C210.50.%20Parse%20it%20as%20a%20decimal%2C%20never%20as%20a%20float%20you%20then%20round%20%E2%80%94%20use%20a%20decimal%20library%20or%20store%20it%20as%20a%20string%20and%20sum%20with%20integer%20pence.%0A-%20amounts%20is%20a%20list.%20Sum%20the%20values%20per%20currency%3B%20a%20payout%20usually%20has%20one%20currency%20but%20do%20not%20assume%20it.%20Show%20each%20currency%20total%20on%20the%20row.%0A-%20The%20payload%20has%20no%20payout%20id%20and%20no%20store%20name.%20Use%20the%20svix-id%20header%20as%20the%20unique%20key%20for%20the%20row.%0A-%20bankAccount%20contains%20full%20bank%20details.%20Store%20only%20the%20last%20four%20characters%20of%20accountNumber%20\(or%20of%20IBAN%20if%20accountNumber%20is%20missing\)%20and%20the%20bankAccount.id.%20Do%20not%20store%20or%20display%20the%20full%20IBAN%20or%20account%20number%20anywhere%2C%20including%20logs.%0A-%20estimatedArrivalDate%20is%20a%20%22month%2Fyear%22%20string%20such%20as%20%221%2F2024%22%2C%20or%20an%20empty%20string%20when%20unknown.%20Show%20it%20as%20text%3B%20do%20not%20parse%20it%20as%20a%20date.%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%20row%20and%20return%20200%20immediately.%20Ignore%20any%20svix-id%20already%20saved.%20Handle%20payments.payout.created%3B%20save%20and%20acknowledge%20any%20other%20event%20type%20but%20do%20nothing%20with%20it.%0A2.%20A%20ledger%20table%20with%3A%20received%20time%20\(UTC%2C%20display%20in%20TIMEZONE\)%2C%20amounts%20per%20currency%2C%20bank%20account%20last%20four%2C%20bank%20account%20id%2C%20estimated%20arrival%20date%2C%20reconciled%20\(yes%2Fno\)%2C%20reconciled%20on%2C%20statement%20reference%20\(free%20text\)%2C%20notes%20\(free%20text\).%0A3.%20GET%20%2F%20%E2%80%94%20protected%20by%20HTTP%20basic%20auth%20with%20ADMIN_PASSWORD.%20A%20table%20of%20payouts%20newest%20first%20with%20a%20month%20filter%20\(default%3A%20current%20month\)%2C%20showing%20the%20columns%20above.%20Each%20row%20has%20a%20Reconciled%20tick-box%20and%20editable%20statement%20reference%20and%20notes%20fields%20that%20save%20without%20a%20page%20reload.%20Rows%20received%20more%20than%207%20days%20ago%20and%20not%20reconciled%20are%20highlighted%20amber.%20Above%20the%20table%3A%20total%20paid%20out%20this%20month%20per%20currency%2C%20total%20reconciled%2C%20total%20outstanding.%0A4.%20Download%20CSV%20for%20the%20filtered%20month%2C%20one%20row%20per%20payout%2C%20columns%3A%20received_date%2C%20currency%2C%20amount%2C%20bank_last4%2C%20estimated_arrival%2C%20reconciled%2C%20reconciled_on%2C%20statement_reference%2C%20notes.%20Amounts%20as%20plain%20decimals%20with%20two%20places%20and%20no%20currency%20symbol%20so%20bookkeeping%20software%20imports%20them%20cleanly.%20If%20a%20payout%20has%20more%20than%20one%20currency%2C%20write%20one%20CSV%20row%20per%20currency.%0A5.%20Optional%20weekly%20reminder%3A%20if%20SLACK_WEBHOOK_URL%20is%20set%2C%20every%20Monday%20at%2009%3A00%20in%20TIMEZONE%20post%20%22N%20payouts%20totalling%20X%20not%20yet%20reconciled%2C%20oldest%20from%20%3Cdate%3E%22%2C%20or%20nothing%20if%20there%20are%20none.%20Skip%20entirely%20if%20the%20variable%20is%20unset.%0A6.%20GET%20%2Fhealth%20that%20returns%20200%20and%20%7B%20%22ok%22%3A%20true%20%7D%20and%20nothing%20else.%0A%0ADEFINITION%20OF%20DONE%0A%0A-%20%5B%20%5D%20The%20example%20payload%20above%20creates%20one%20row%20showing%204%2C240.50%20GBP%20with%20estimated%20arrival%20%221%2F2024%22.%0A-%20%5B%20%5D%20The%20same%20event%20delivered%20twice%20creates%20one%20row.%0A-%20%5B%20%5D%20A%20bad%20signature%20returns%20400%20and%20records%20nothing.%0A-%20%5B%20%5D%20Nothing%20in%20the%20database%2C%20page%20or%20logs%20contains%20the%20full%20IBAN%20or%20account%20number%20%E2%80%94%20only%20%225678%22%20style%20last-four%20values.%0A-%20%5B%20%5D%20Ticking%20Reconciled%20saves%20immediately%20and%20survives%20a%20reload%20and%20a%20restart.%0A-%20%5B%20%5D%20The%20CSV%20opens%20in%20a%20spreadsheet%20with%20amounts%20as%20numbers.%0A-%20%5B%20%5D%20A%20payout%20received%20more%20than%20a%20week%20ago%20and%20not%20reconciled%20is%20highlighted.%0A%0AWHAT%20TO%20GIVE%20ME%20WHEN%20YOU%20ARE%20DONE%0A%0A1.%20The%20public%20webhook%20URL%20\(%E2%80%A6%2Fwebhooks%2Fstorekit\)%20and%20the%20ledger%20URL.%0A2.%20The%20environment%20variables%20to%20set%20\(STOREKIT_WEBHOOK_SECRET%2C%20ADMIN_PASSWORD%2C%20TIMEZONE%2C%20optionally%20SLACK_WEBHOOK_URL\)%20and%20where%20to%20set%20them%20on%20this%20platform.%0A3.%20Confirmation%20that%20the%20app%20stays%20up%2024%20hours%20a%20day.%0A)

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

> **Volledige prompt (klik om uit te klappen)**
>
> ```text theme={null}
> I want a small private web app that keeps a ledger of the payouts storekit sends to my bank account, so I can reconcile them against my bank statement. storekit tells the app about each payout by webhook. The app must never lose a payout, never record the same one twice, and never make storekit wait.
>
> Build it with a database so the ledger survives restarts and an always-on public HTTPS URL.
>
> ```
>
> ```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 EVENT THIS APP USES: payments.payout.created
>
> Ignore the order example above; this app only cares about payments.payout.created, which looks like this:
>
> {
>   "event": "payments.payout.created",
>   "data": {
>     "amounts": [
>       { "currency": "GBP", "value": "3210.50" },
>       { "currency": "GBP", "value": "1030.00" }
>     ],
>     "bankAccount": {
>       "id": "db97e205-105c-42ff-8460-d06c25cb6830",
>       "IBAN": "GB15HBUK40127612345678",
>       "accountNumber": "0001234",
>       "branchCode": "001234",
>       "currency": "GBP"
>     },
>     "estimatedArrivalDate": "1/2024"
>   }
> }
>
> Important differences from the order events:
> - Money here is NOT in minor units. Each value is a decimal string in major units: "3210.50" means 3,210.50. Parse it as a decimal, never as a float you then round — use a decimal library or store it as a string and sum with integer pence.
> - amounts is a list. Sum the values per currency; a payout usually has one currency but do not assume it. Show each currency total on the row.
> - The payload has no payout id and no store name. Use the svix-id header as the unique key for the row.
> - bankAccount contains full bank details. Store only the last four characters of accountNumber (or of IBAN if accountNumber is missing) and the bankAccount.id. Do not store or display the full IBAN or account number anywhere, including logs.
> - estimatedArrivalDate is a "month/year" string such as "1/2024", or an empty string when unknown. Show it as text; do not parse it as a date.
>
> 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 row and return 200 immediately. Ignore any svix-id already saved. Handle payments.payout.created; save and acknowledge any other event type but do nothing with it.
> 2. A ledger table with: received time (UTC, display in TIMEZONE), amounts per currency, bank account last four, bank account id, estimated arrival date, reconciled (yes/no), reconciled on, statement reference (free text), notes (free text).
> 3. GET / — protected by HTTP basic auth with ADMIN_PASSWORD. A table of payouts newest first with a month filter (default: current month), showing the columns above. Each row has a Reconciled tick-box and editable statement reference and notes fields that save without a page reload. Rows received more than 7 days ago and not reconciled are highlighted amber. Above the table: total paid out this month per currency, total reconciled, total outstanding.
> 4. Download CSV for the filtered month, one row per payout, columns: received_date, currency, amount, bank_last4, estimated_arrival, reconciled, reconciled_on, statement_reference, notes. Amounts as plain decimals with two places and no currency symbol so bookkeeping software imports them cleanly. If a payout has more than one currency, write one CSV row per currency.
> 5. Optional weekly reminder: if SLACK_WEBHOOK_URL is set, every Monday at 09:00 in TIMEZONE post "N payouts totalling X not yet reconciled, oldest from <date>", or nothing if there are none. Skip entirely if the variable is unset.
> 6. GET /health that returns 200 and { "ok": true } and nothing else.
>
> DEFINITION OF DONE
>
> - [ ] The example payload above creates one row showing 4,240.50 GBP with estimated arrival "1/2024".
> - [ ] The same event delivered twice creates one row.
> - [ ] A bad signature returns 400 and records nothing.
> - [ ] Nothing in the database, page or logs contains the full IBAN or account number — only "5678" style last-four values.
> - [ ] Ticking Reconciled saves immediately and survives a reload and a restart.
> - [ ] The CSV opens in a spreadsheet with amounts as numbers.
> - [ ] A payout received more than a week ago and not reconciled is highlighted.
>
> WHAT TO GIVE ME WHEN YOU ARE DONE
>
> 1. The public webhook URL (…/webhooks/storekit) and the ledger URL.
> 2. The environment variables to set (STOREKIT_WEBHOOK_SECRET, ADMIN_PASSWORD, TIMEZONE, optionally SLACK_WEBHOOK_URL) and where to set them on this platform.
> 3. Confirmation that the app stays up 24 hours a day.
> ```

## Stap 2: Verbind storekit

**1. Open the webhooks portal**

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

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

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

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

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

**4. Send a test event**

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

Vink voor dit endpoint alleen `payments.payout.created` aan. Gebruik de testoptie in de portal met dat eventtype om je eerste rij aan te maken.

## Stap 3: Reconciliëren

Wanneer de volgende echte uitbetaling binnenkomt, vergelijk je de rij met **Analytics** → **Accounting** → **Payouts** in het dashboard en met je bankafschrift, en vink hem vervolgens af. Uitbetalingen volgen je [uitbetalingsschema](/docs/nl/payouts/payout-schedule), dus de eerste echte rij kan een paar dagen duren.

## Ideeën

* **Automatisch naar je boekhouder sturen**: vraag de builder om de CSV van de maand op de 1e te mailen via een provider zoals Resend of Postmark.
* **Matchen op bedrag**: plak een bankafschrift-CSV in en laat de app matches voorstellen op basis van bedrag en datum.
* **Meerdere stores**: draai één endpoint per storekit-account en voeg een "store"-label toe uit een environment-variabele zodat het grootboek kan worden samengevoegd.

## Wat dit niet doet

* **Het verklaart niet wat er in een uitbetaling zit.** Het event bevat de totalen, niet de bestellingen, kosten of refunds daarachter; gebruik daarvoor het [feerapport](/docs/nl/guides/reports/fee-report) en het [transactierapport](/docs/nl/guides/reports/transactions-report).
* **Het begint op de dag dat je het verbindt.** Eerdere uitbetalingen staan in het dashboard, niet in de eventstream.
* **Het is geen betalingsbewijs.** Het event zegt dat er een uitbetaling is aangemaakt; of en wanneer deze aankomt is tussen de betalingsprovider en je bank.
* **Het is jouw service.** storekit-support kan bevestigen dat het event je URL heeft bereikt en de responscode; het grootboek en de export zijn van jou.

## Gerelateerd

> **[payments.payout.created-payload](/docs/nl/developers/webhooks/webhook-events#payments-payout-created)**
>
> De volledige payload, inclusief de velden die deze recipe bewust niet opslaat.

> **[Hoe uitbetalingen werken](/docs/nl/payouts/overview)**
>
> Timing, kosten en waar uitbetalingen in het dashboard verschijnen.

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

> **[Feerapport](/docs/nl/guides/reports/fee-report)**
>
> Wat er is ingehouden vóór de uitbetaling.


## Related topics

- [Terugbetalingen & uitbetalingen](/docs/nl/guides/payments/refunds-payouts.md)
- [Terugbetalingen en transacties voor betaallinks](/docs/nl/payment-links/refunds-and-transactions.md)
- [Kostenrapport & Daily Summary](/docs/nl/guides/reports/fee-report.md)
- [Delivery- en pickup-store](/docs/nl/getting-started/store-types/delivery-pickup-store.md)
- [storekit Payments instellen](/docs/nl/guides/payments/storekit-payments-setup.md)


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