Tikkie Killer — Hackathon Submission
Inspiration
In the Netherlands, "Tikkie" — a payment-request app — became so dominant that "een tikkie sturen" is now a verb. But the moment everyone's at the table debating who had the third beer or whether to include the service charge, the fairness falls apart: someone always over-pays, and the friend who's bad with math sneaks out paying for one item less. We wanted to make the fairness part disappear. Snap a photo, tap your items, done.
What it does
Tikkie Killer turns a restaurant receipt into precisely-targeted bunq payment requests in three taps:
- Snap the bill — Claude 3.5 Sonnet on AWS Bedrock reads every line, understands Dutch quantity formats (
3x Bier 13,50), skips BTW/service rows, normalises printer abbreviations (BTTRBLN→ Bitterballen), and returns clean structured JSON. - Everyone claims their items via a shared link. Identical items collapse into a single row (
Bier ×3) with a per-person stepper, or you can flip to a step-based mode that walks the table item-by-item. No login, just open the link on a second phone and start tapping. - Send — bunq
RequestInquirypings each participant for the exact amount they owe. Non-bunq friends get abunq.mefallback link.
How we built it
- Frontend — Vue 3 + TypeScript + Tailwind, themed end-to-end with bunq's design tokens (colors, radii, type scale) so it feels native to the bunq family. Multi-bill dashboard backed by localStorage, parametric
/bill/:tokenroutes for shareable links, a contact-picker that remembers people you've split with before. - Backend — Laravel 13 in Docker, with three thin services:
BedrockServicecalls Claude 3.5 Sonnet via the AWS SDK with a tightly-scoped Dutch-receipt prompt that forces strict JSON output and silently falls back to a mock if the model errors.BunqServicewraps the bunq PHP SDK, issuing oneRequestInquiryper recipient, with theApiContextcached to disk to stay within rate limits.SessionControllerexposes share-code endpoints (8-char alphabetic, no ambiguous0/O/1/I/L) so multiple users can claim items concurrently against the same bill.
- Image pipeline — base64 over JSON, magic-byte sniffing on the server to set the right
media_typefor Bedrock (JPEG/PNG/WebP/HEIC), 1KB–5MB validation to fail fast on garbage uploads. - Live multi-user — polling every 2–3s on the split view. Websockets were tempting, but for a 24-hour hackathon polling is the boring choice that always works.
Challenges we ran into
- Bedrock model access — Claude 3.5 Sonnet isn't on by default in workshop accounts. The first half-hour was waiting for the access request to flip green.
- Temporary AWS credentials — workshop accounts hand out short-lived creds with a session token. The default Bedrock client config didn't pass
AWS_SESSION_TOKENand calls silently failed until we added explicit token support. - Dutch receipts are messy — comma decimals, BTW/service lines mixed in with consumables, abbreviated names,
3xquantity rows that collapse different things on different printers. Multiple prompt iterations to get reliable parsing. - Image format detection — phone uploads come in as HEIC, drag-and-drops as PNG, screenshots as WebP. Hard-coding
image/jpegfor the Bedrockmedia_typesilently failed for everything except actual JPEGs. Fix: sniff the first four bytes and set the type accordingly. - The silent fallback that masked everything —
BedrockServicefalls back to mock data on any failure (so the demo never breaks). Great in production, awful during integration: we spent twenty minutes debugging "why does the AI always sayBier, Wijn, Bitterballen?" before realising every Bedrock call was failing and the mock was hiding it. - Three devs, overlapping branches — CORS, env vars, route names all touch each other. We locked an API contract on day one in a sprint doc, so frontend and backend could ship against mocks in parallel and meet cleanly at integration.
Accomplishments that we're proud of
- An end-to-end photo-to-payment pipeline working in under a day, on real bar receipts, with the exact amount each friend owes.
- A UI that feels like bunq, not like a hackathon prototype — full design-token system, confident typography, clean multi-bill dashboard, no emoji decoration.
- Multi-user splitting with no auth, no setup. Open the share-link on a second phone and start claiming; the host's screen updates within 3 seconds.
- Strict JSON output from Claude on the first try. Zero parser fallbacks needed in the normal path — the prompt itself is the contract.
What we learned
- Vision LLMs replace OCR pipelines for free-form documents. One vision call beats OCR + regex + heuristics for messy real-world receipts, and handles abbreviations and quantity expansion that traditional pipelines miss entirely.
- The prompt is an API. When the LLM is part of your backend, the prompt has a strict shape, explicit edge-cases (
skip BTW, expand quantities, comma → dot), and a refusal to output anything outside the JSON envelope. Treat it like a schema, not a vibe. - Mock-first integration. Locking the JSON contract in a doc and shipping mocks before the real endpoints existed let three people work in parallel and meet at integration with almost no surprises.
- Boring choices win on the clock. Polling > websockets. Token-based share-links > auth. A working demo path > a feature-complete app.
What's next for Tiskkie Killer
- Real-time without polling — Laravel Reverb for live claims, so the host sees every tap land instantly across six phones.
- Authentication + persistent payments — link a bunq account once, replay any past split, see who actually paid.
- AI clarifying questions — when a line is unreadable, the model asks the host instead of guessing ("did this say Bitterballen or Bittergarnituur?").
- Talk to Pay — voice input via AWS Transcribe, intent-extraction with Claude ("Tikkie Djeno 12 euro voor de bier"), one-tap confirm.
- Beyond bunq — open-banking integrations so the receiving side works for any bank, not just bunq.
- Progress visualisation — a "house-building" metaphor for the running balance, so closing out a bill feels like finishing something, not just paying.
Built With
- code
Log in or sign up for Devpost to join the conversation.