Files · Mistral AI Document Pipeline for NetSuite Invoice Automation
72 (1 binary, 570.7 kB total)attempt 2
README.md·4316 B·markdown
markdown
# Mistral AI Document Pipeline for NetSuite Invoice Automation
> Automatically extracts line items and vendor details from incoming invoice PDFs and posts them to NetSuite as draft vendor bills.
## Overview
SMB finance teams manually key invoice data from PDF attachments into NetSuite, wasting hours and introducing errors. This pipeline solves that by:
1. Ingesting invoice PDFs through a NetSuite webhook
2. Extracting text via **unpdf** (digital PDFs) or **tesseract.js** + **sharp** (scanned images)
3. Parsing structured data with **Mistral AI** (`@mistralai/mistralai`)
4. Repairing malformed JSON output with **`@reaatech/structured-repair-core`**
5. Posting vendor bills to NetSuite via OAuth 1.0 authenticated REST API
6. Preventing duplicate processing with **`@reaatech/agent-memory`** (SHA-256 hash idempotency)
7. Tracing the full pipeline with **Langfuse** observability
## Architecture
```
NetSuite Webhook → app/api/invoice-webhook/route.ts
↓
src/services/pdf-extractor.ts (unpdf / tesseract.js + sharp)
↓
src/services/document-extractor.ts (MistralParser — @mistralai/mistralai)
↓
src/services/invoice-repairer.ts (@reaatech/structured-repair-core)
↓
src/lib/netsuite-api.ts (OAuth 1.0 → NetSuite REST API)
↓
src/services/memory-service.ts (@reaatech/agent-memory — idempotency)
```
## Prerequisites
Set these environment variables in `.env.local` (see `.env.example`):
| Variable | Description |
|----------|-------------|
| `MISTRAL_API_KEY` | Mistral AI API key |
| `NETSUITE_ACCOUNT_ID` | NetSuite account ID for OAuth realm |
| `NETSUITE_CONSUMER_KEY` | OAuth 1.0 consumer key |
| `NETSUITE_CONSUMER_SECRET` | OAuth 1.0 consumer secret |
| `NETSUITE_TOKEN_ID` | OAuth 1.0 token ID |
| `NETSUITE_TOKEN_SECRET` | OAuth 1.0 token secret |
| `NETSUITE_BASE_URL` | NetSuite REST API base URL |
| `OPENAI_API_KEY` | OpenAI key (for agent-memory embeddings) |
| `LANGFUSE_PUBLIC_KEY` | Langfuse public key (observability) |
| `LANGFUSE_SECRET_KEY` | Langfuse secret key |
| `INVOICE_WEBHOOK_SECRET` | Shared secret for webhook auth |
| `NETSUITE_VENDOR_BILL_CUSTOM_FORM_ID` | Optional custom form ID for NetSuite vendor bills |
| `NEXT_PUBLIC_APP_URL` | Public app URL for webhook callbacks |
## API Reference
### `POST /api/invoice-webhook`
Receives invoice PDFs from a NetSuite webhook.
**Request:** `multipart/form-data`
- `pdf` (File, required) — Invoice PDF file
- `isScanned` (string, optional) — `"true"` for scanned/image PDFs (uses OCR)
- `webhookSecret` (string, optional) — Auth secret matching `INVOICE_WEBHOOK_SECRET`
**Response (200):** `{ "status": "success", "invoiceId": "123", "hash": "abc..." }`
**Response (200 duplicate):** `{ "status": "duplicate", "hash": "abc..." }`
**Response (400):** `{ "status": "error", "error": "...", "stage": "extraction" }`
**Response (500):** `{ "status": "error", "error": "...", "stage": "repair|netsuite|memory" }`
### `GET /api/invoice-webhook`
Health check endpoint.
**Response:** `{ "status": "ok" }`
## Tech Stack
- **Framework:** Next.js 16 (App Router)
- **AI:** `@mistralai/mistralai` (Mistral Large)
- **PDF:** `unpdf`, `sharp`, `tesseract.js`
- **Validation:** `zod`
- **Repair:** `@reaatech/structured-repair-core`
- **Memory:** `@reaatech/agent-memory`
- **Observability:** `langfuse`
- **Testing:** `vitest` + `msw`
## Development
```bash
pnpm install # install dependencies
pnpm dev # start dev server
pnpm build # production build
pnpm typecheck # TypeScript type checking
pnpm lint # ESLint
pnpm test # vitest run with coverage
```
## Project Layout
```
app/api/invoice-webhook/route.ts Webhook endpoint
src/schemas/ Zod schemas for invoice data
src/lib/ Hash utilities, NetSuite API client, observability
src/services/ PDF extraction, Mistral parsing, JSON repair, memory, pipeline
src/instrumentation.ts Next.js instrumentation (Langfuse init)
tests/ Vitest suite (mirrors src/)
```
## License
MIT — see [LICENSE](./LICENSE).