Extracter is a full-stack web application that uses AI to automatically scan and extract structured data from receipt images and PDFs. Upload a receipt, and within seconds the app surfaces the merchant name, transaction details, itemised line items, and a plain-English summary β all stored securely in the cloud and tied to your account.
- Features
- Tech Stack
- Architecture Overview
- How It Works
- Data Model
- Project Structure
- Prerequisites
- Getting Started
- Environment Variables
- Available Scripts
- Pricing Plans
- Deployment
- Drag-and-drop upload β Drop a receipt image (JPEG, PNG, WebP) or PDF directly onto the upload zone.
- AI data extraction β Google Gemini automatically parses merchant information, transaction dates, amounts, currency, and individual line items.
- Receipt dashboard β Browse all your uploaded receipts with at-a-glance status indicators (Pending / Processed / Error).
- Receipt detail view β Inspect every extracted field and read a human-friendly summary of the receipt.
- Download & delete β Download the original file or permanently remove a receipt from storage and the database.
- Authentication β Secure sign-up / sign-in via Clerk; each user can only access their own receipts.
- Usage-based billing β Tiered pricing (Free, Basic, Team) enforced through Schematic feature flags.
- Real-time updates β Convex pushes database changes to the UI instantly without polling.
| Layer | Technology |
|---|---|
| Framework | Next.js 15 (App Router) |
| UI | React 19, Tailwind CSS v4, shadcn/ui |
| Database & Backend | Convex |
| Authentication | Clerk |
| AI Processing | Google Gemini 2.0 Flash Lite via Inngest Agent Kit |
| Background Jobs | Inngest |
| Feature Flags / Billing | Schematic |
| Language | TypeScript |
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Browser β
β Landing Page β Dashboard β Receipt Detail β
ββββββββββββββββββββββββ¬βββββββββββββββββββββββββββββββββββ
β HTTPS
ββββββββββββββΌβββββββββββββ
β Next.js Server β
β (App Router / API) β
βββββ¬βββββββββββ¬βββββββββββ
β β
βββββββββΌββββ βββββΌβββββββββββββββ
β Convex β β Inngest β
β (DB + RPC)β β (background jobs) β
βββββββββββββ ββββββββ¬ββββββββββββ
β
βββββββββββββββΌβββββββββββββββ
β Agent Network β
β ββββββββββββββββββββββ β
β β Receipt Scanning β β
β β Agent β β
β β (Gemini + OCR) β β
β ββββββββββ¬ββββββββββββ β
β β extracted JSON β
β ββββββββββΌββββββββββββ β
β β Database Agent β β
β β (saves to Convex) β β
β ββββββββββββββββββββββ β
ββββββββββββββββββββββββββββββ
-
Upload β The user drags a receipt file onto the upload zone. The file is uploaded directly to Convex Storage via a pre-signed URL, and a receipt record is inserted into the
reciptstable withstatus: "pending". -
Trigger β The upload action fires an Inngest event (
extract-data-from-pdf-and-save-to-database) carrying the temporary file URL and the receipt's database ID. -
AI Agent Network β Inngest runs the agent network:
- Receipt Scanning Agent downloads the file, converts it to base64, and sends it to Gemini with a structured extraction prompt. The model returns a JSON object containing merchant details, transaction info, line items, and totals.
- Database Agent receives the extracted JSON and calls the Convex
updateReceiptWithExtractDatamutation to persist the data and flip the record's status to"proceed".
-
Real-time update β Because Convex is reactive, the dashboard and detail pages update automatically when the record changes β no page reload required.
-
Usage tracking β Each successful scan is tracked via Schematic so that plan limits can be enforced.
The recipts table (Convex schema):
| Field | Type | Description |
|---|---|---|
userId |
string |
Clerk user subject (owner of the receipt) |
fileName |
string |
Original file name |
fileDisplayName |
string? |
Human-readable name assigned by the AI |
fileId |
Id<"_storage"> |
Convex Storage reference |
uploadedAt |
number |
Unix timestamp of upload |
size |
number |
File size in bytes |
mimeType |
string |
MIME type (e.g. image/jpeg, application/pdf) |
status |
string |
"pending" β "proceed" β "error" |
merchantName |
string? |
Extracted merchant / store name |
merchantAddress |
string? |
Extracted merchant address |
merchantContact |
string? |
Extracted phone / email |
transactionDate |
string? |
Date of the transaction (YYYY-MM-DD) |
transactionAmount |
string? |
Total amount paid |
currency |
string? |
Currency code (e.g. USD) |
reciptSummary |
string? |
Plain-English AI-generated summary |
items |
array |
Line items β { name, quantity, unitPrice, totalPrice } |
| Name | Type | Description |
|---|---|---|
generateUploadUrl |
mutation | Returns a short-lived upload URL for Convex Storage |
storeReciepts |
mutation | Inserts a new receipt record |
getRecipts |
query | Returns all receipts for a given user (descending order) |
getReceiptById |
query | Returns a single receipt (auth-checked) |
getReceiptDownloadUrl |
query | Returns a temporary download URL for the stored file |
updateReceiptsStatus |
mutation | Updates only the status field (auth-checked) |
deleteReciept |
mutation | Deletes the file from Storage and the DB record (auth-checked) |
updateReceiptWithExtractData |
mutation | Persists all AI-extracted fields and sets status to "proceed" |
extracter/
βββ app/
β βββ actions/ # Next.js Server Actions
β β βββ getFileDownloadUrl.ts
β β βββ getTemporaryAcessToken.ts
β βββ dashboard/ # Protected dashboard page
β βββ manage-plan/ # Billing / plan management page
β βββ receipt/[id]/ # Dynamic receipt detail page
β βββ server/ # Inngest serve endpoint
β βββ layout.tsx # Root layout (Clerk + Convex providers)
β βββ page.tsx # Landing page
βββ components/
β βββ Dashboard/ # Dashboard-specific components
β βββ Landing/ # Landing page sections (Hero, Feature, Pricing, Footer)
β βββ schematic/ # Schematic billing components
β βββ ui/ # shadcn/ui primitive components
β βββ comp-145.tsx # Feature card component
β βββ comp-548.tsx # Custom dropzone component
β βββ ConvexClientProvider.tsx # Convex React provider wrapper
βββ convex/
β βββ schema.ts # Database schema
β βββ recipts.ts # All queries & mutations
β βββ auth.config.ts # Clerk JWT configuration
βββ inngest/
β βββ agents/
β β βββ receiptScanningAgent.ts # Gemini-powered OCR agent
β β βββ databaseAgent.ts # Convex persistence agent
β βββ agent.ts # Agent network + Inngest function
β βββ client.ts # Inngest client
β βββ constants.ts # Event name constants
βββ hooks/ # Custom React hooks
βββ lib/ # Shared utilities (Convex client, Schematic client)
βββ middleware.ts # Clerk auth middleware
βββ next.config.ts
βββ package.json
- Node.js β₯ 18
- pnpm β₯ 10 (or npm / yarn)
- A Convex account β convex.dev
- A Clerk account β clerk.com
- A Google AI API key (Gemini) β aistudio.google.com
- An Inngest account β inngest.com
- A Schematic account β schematichq.com (optional β for billing)
git clone https://github.com/realsudarshan/extracter.git
cd extracterpnpm installnpx convex dev --until-successThis will prompt you to log in and create a new Convex project. After it succeeds, a .env.local file is created with NEXT_PUBLIC_CONVEX_URL.
- Create an application in the Clerk dashboard.
- Add your Clerk publishable and secret keys to
.env.local(see Environment Variables). - In the Clerk dashboard, create a JWT template named
convexfollowing the Convex + Clerk guide. - In
convex/auth.config.ts, uncomment the Clerk provider and paste your Clerk Issuer URL. - Add
CLERK_JWT_ISSUER_DOMAINto your Convex deployment environment variables via the Convex dashboard.
- Generate an API key at aistudio.google.com/apikey.
- Add it to
.env.localasGOOGLE_AI_API_KEY.
- Sign up at app.inngest.com and create an app.
- Copy your Event Key and Signing Key.
- Add them to
.env.local(see Environment Variables).
- Create an account at schematichq.com and set up your plans.
- Add your Schematic API key to
.env.local.
pnpm devThis runs the Next.js frontend (next dev) and the Convex backend (convex dev) in parallel.
Open http://localhost:3000 in your browser.
Create a .env.local file in the project root:
# Convex (auto-generated by `npx convex dev`)
NEXT_PUBLIC_CONVEX_URL=https://<your-deployment>.convex.cloud
# Clerk
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_test_...
CLERK_SECRET_KEY=sk_test_...
# Google AI (Gemini)
GOOGLE_AI_API_KEY=AIza...
# Inngest
INNGEST_EVENT_KEY=...
INNGEST_SIGNING_KEY=...
# Schematic (optional)
NEXT_PUBLIC_SCHEMATIC_PUBLISHABLE_KEY=...
SCHEMATIC_API_KEY=...| Command | Description |
|---|---|
pnpm dev |
Start Next.js and Convex dev servers concurrently |
pnpm build |
Build the Next.js app for production |
pnpm start |
Start the production server |
pnpm lint |
Run ESLint |
| Plan | Price | Users |
|---|---|---|
| Free | Free forever | 1 |
| Basic | $4 / month or $40 / year | 2 |
| Team | $7 / month or $70 / year | 5 |
Plan management is handled on the /manage-plan page and enforced via Schematic feature flags.
The Convex backend is deployed automatically when you run npx convex deploy or link your repository in the Convex dashboard.
Deploy the Next.js app to Vercel (recommended):
- Push the repository to GitHub.
- Import the project in the Vercel dashboard.
- Add all environment variables from
.env.localto the Vercel project settings. - Deploy.
Register the Inngest serve endpoint with your production app URL in the Inngest dashboard. The endpoint is hosted at /api/inngest (configured in app/server/).