The brief
Chantelle México needed a website for its promotion with El Palacio de Hierro. Participants needed to register a purchase and upload a receipt.
The campaign team needed to review those entries and retain an official record. Marketing needed to distinguish completed registrations from visits and submit-button clicks.
I worked on product requirements, interface design, implementation and integrations. Delivery ran from September to October 2026.
Product goals and requirements
The goal was to make registration simple and keep each accepted entry traceable. The brief called for an editorial, mobile-first experience aligned with both brands.
| Audience | Requirement | Implementation |
|---|---|---|
| Participant | Register a purchase from a phone | Store selection, receipt guidance, progress and error states |
| Campaign team | Find entries and inspect receipts | Sheets with receipt links, readable statuses, review and comment columns |
| Marketing | Measure saved registrations | CompleteRegistration runs after the database accepts an entry |
| Brand and legal | Use approved assets and wording | Official photography, partner logos and original approved PDFs |
| Infrastructure | Recover from integration failures | Database records, a delivery queue and retries |
“Prevent duplicate tickets” needed a precise definition. The implementation rejects the same uploaded image hash within a campaign. It does not compare different photographs through OCR.
Email and phone are not unique participation limits. A participant can use the same email with another receipt image.
Registration experience
Campaign photography and the Chantelle × El Palacio de Hierro logos introduce the promotion. The form collects contact information, store, purchase date and required consent. Marketing opt-in is separate.
Participants can select or drag a receipt image. The interface checks its type and size, shows a preview, and lets them replace or remove it. It accepts JPG, PNG, HEIC and HEIF files up to 4 MB.
The stages “Preparar,” “Subir foto” and “Generar folio” explain progress during submission.

The confirmation says the team received the registration and asks the participant to retain the ticket. It explains that the team will contact them about results.
The database creates an internal folio. The current public response confirms success without returning it. Submission does not establish eligibility or a winning entry.

Architecture
Supabase stores the official participation record. UploadThing stores private receipts. Google Sheets receives a copy for review.
| Step | Connection | Behavior |
|---|---|---|
| Prepare | Browser → Next.js → Supabase | Check origin, request limits and campaign status. Create a signed upload intent valid for 15 minutes. |
| Upload | Browser → UploadThing | Transfer the image directly. Check the provider callback and attach file metadata to the intent. |
| Save | Next.js → Supabase | Create participation, internal folio and delivery event. Consume the intent in the same transaction. |
| Confirm | Next.js → participant | Return success after saving. Start integration work after the response. |
| Review | Delivery queue → Apps Script → Sheets | Insert or update by folio. Preserve manual fields and retry failed deliveries. |
| Measure | Browser Pixel + server CAPI → Meta | Share event IDs and respect measurement opt-out. |
The application uses Next.js App Router, React, TypeScript and Zod. Vercel hosts the application. Supabase provides PostgreSQL storage.
Zod checks untrusted input. Database constraints and the finalization function enforce the rules for saved entries.
Receipt uploads travel directly to UploadThing rather than through the application server. Pages render dynamically for a per-request security nonce. Static resources remain cacheable.
Campaign operations
A spreadsheet failure must not discard a saved registration. The database stores each participation and its delivery event together.
The outbox is a durable queue of integration work. The application processes a small batch after responding. A daily Vercel job retries pending deliveries.
Apps Script checks an HMAC signature and inserts or updates rows by folio. Supabase supplies automatic columns. The team retains Revisión and Comentarios.
Retries use increasing delays, with a maximum of 20 attempts. Each batch sends one write at a time to accommodate Apps Script’s document lock.
Operational feedback led to a timeout increase from 15 to 30 seconds. This allowed more time for slow starts and serialized writes.
The Sheet also contains signed receipt links. The application checks a link before requesting a temporary URL from UploadThing. Anyone with that signed link can view the receipt, so the Sheet requires restricted sharing.
The team can export CSV from Sheets. An authenticated administrative dashboard remains planned work.
Security and campaign rules
The application rejects invalid files and expired intents. Origin checks, a honeypot and persistent request limits constrain automated submissions. Registration stops if the database cannot enforce the request limit.
Campaign tables use forced row-level security and grant no access to browser roles. Server credentials handle sensitive operations. Receipt responses disable caching and search indexing.
The interface and server share the Mexico City cutoff. The database also checks campaign status and dates during finalization. A boundary test covers midnight at the cutoff.
The site serves the approved contest rules and privacy notice as PDFs at their legal routes.
Measurement
The site sends PageView and CompleteRegistration through Meta Pixel and Conversions API. Browser and server events share an ID for deduplication.
CompleteRegistration runs only after the database saves the entry. The server normalizes and hashes email and Mexican phone values for matching.
The integration excludes receipts, store, purchase date and internal folio. Measurement opt-out disables browser and server delivery. Hashed identifiers still support matching and are not anonymous data.
Tests cover event construction, matching IDs, hashing and opt-out. Final attribution and deduplication rates require checks in Meta.
Delivery timeline
The original plan estimated 10–12 business days of construction and 2–3 days of review. The history records delivery dates, not working hours.
| Date in 2026 | Delivery |
|---|---|
| September 1 | Application foundation and initial campaign experience |
| September 4 | Supabase request limits and receipt-upload improvements |
| September 8–15 | Editorial design, official photography, consent and confirmation |
| September 17–18 | National store coverage and Sheets synchronization |
| September 26–28 | Receipt access, serialized writes, retries and duplicate-image messaging |
| September 30–October 1 | Tracking, campaign cutoff, approved PDFs and Meta CAPI |
These iterations gave the team specific parts to review: design, upload, operations and measurement. The record does not establish an AI-related reduction in effort.
Development with AI and specifications
AI assisted implementation. Product requirements defined participation, necessary fields, duplicate handling and event timing. Client and legal decisions remained explicit dependencies.
The repository documents this sequence: discovery, specification, plan, implementation, harness, evidence and saved context. The intake identifies requirements R-01 through R-10.
Mini-specs describe rules, exclusions, failures and acceptance criteria. Architecture, security and operations documents retain context between development sessions.
For example, tracking had four requirements:
- Send CompleteRegistration after saving the participation.
- Reuse the browser event ID.
- Transform the matching identifiers.
- Respect measurement opt-out.
The route handler implements those behaviors, and tests check them. This gives the AI task a concrete result to satisfy.
The guide also describes bounded subagent tasks and safe context retrieval. It does not establish their use in every commit or a deployed RAG service.
Verification and remaining work
The harness runs lint, TypeScript, Vitest and a production build. All stages passed on October 7, 2026, with 15 tests across six files.
Tests cover validation, 16 stores, honeypot behavior, receipt signatures, Sheets rows, campaign cutoff and Meta event behavior. Captures show the locally rendered interface.
This audit did not submit a production entry or repeat authenticated-provider checks. Build success alone does not verify upload privacy, legal approval or complete registration.
The delivered work includes the campaign interface, private receipt handling, database registration, Sheets operations, legal routes and measurement integrations.
Planned work includes the administrative dashboard, automated retention and deletion, and broader provider and concurrency checks. An email adapter exists, but delivery was not verified here.
Registration volume, completion rate, review time and campaign attribution remain separate measurements. Older foundation documents retain some pending items that later commits implemented.
Work together
Planning a similar project? Estimate your project or review the related services.
