Installation
Copy
npm install parsefy zod
- parsefy: Parsefy SDK for document extraction
- zod: TypeScript-first schema validation library
Environment setup
Add your API key to.env.local:
Copy
PARSEFY_API_KEY=pk_your_api_key
Server Action
Create a server action to handle document extraction:Copy
// app/actions/extract.ts
'use server';
import { Parsefy } from 'parsefy';
import * as z from 'zod';
const client = new Parsefy();
const invoiceSchema = z.object({
// REQUIRED - triggers fallback if below confidence threshold
invoice_number: z.string().describe('The invoice number'),
total: z.number().describe('Total amount including tax'),
// OPTIONAL - won't trigger fallback if missing
date: z.string().optional().describe('Invoice date'),
vendor: z.string().optional().describe('Vendor name'),
line_items: z.array(z.object({
description: z.string(),
quantity: z.number(),
amount: z.number(),
})).optional().describe('Line items'),
});
export async function extractInvoice(formData: FormData) {
const file = formData.get('file') as File;
const { object, metadata, verification, error } = await client.extract({
file,
schema: invoiceSchema,
confidenceThreshold: 0.85, // default
enableVerification: true, // Enable math verification
});
if (error) {
return { error: error.message };
}
return {
data: object,
confidence: object?._meta.confidence_score,
fieldConfidence: object?._meta.field_confidence,
fallbackTriggered: metadata.fallbackTriggered,
verification: verification,
};
}
React component
Create a form component to upload documents:Copy
// app/components/InvoiceUploader.tsx
'use client';
import { useState } from 'react';
import { extractInvoice } from '../actions/extract';
export function InvoiceUploader() {
const [result, setResult] = useState<any>(null);
const [loading, setLoading] = useState(false);
async function handleSubmit(formData: FormData) {
setLoading(true);
const response = await extractInvoice(formData);
setResult(response);
setLoading(false);
}
return (
<div>
<form action={handleSubmit}>
<input type="file" name="file" accept=".pdf,.docx" />
<button type="submit" disabled={loading}>
{loading ? 'Extracting...' : 'Extract'}
</button>
</form>
{result?.data && (
<>
<pre>{JSON.stringify(result.data, null, 2)}</pre>
<p>Confidence: {(result.confidence * 100).toFixed(1)}%</p>
{result.verification && (
<p>Verification: {result.verification.status}</p>
)}
{result.fallbackTriggered && (
<p className="warning">Fallback model was used</p>
)}
</>
)}
{result?.error && (
<p className="error">{result.error}</p>
)}
</div>
);
}
API Route (alternative)
If you prefer API routes over server actions:Copy
// app/api/extract/route.ts
import { NextRequest, NextResponse } from 'next/server';
import { Parsefy } from 'parsefy';
import * as z from 'zod';
const client = new Parsefy();
const invoiceSchema = z.object({
// REQUIRED
invoice_number: z.string().describe('The invoice number'),
total: z.number().describe('Total amount'),
// OPTIONAL
date: z.string().optional().describe('Invoice date'),
});
export async function POST(request: NextRequest) {
const formData = await request.formData();
const file = formData.get('file') as File;
const { object, error, metadata, verification } = await client.extract({
file,
schema: invoiceSchema,
confidenceThreshold: 0.85,
enableVerification: true,
});
if (error) {
return NextResponse.json({ error: error.message }, { status: 422 });
}
return NextResponse.json({
data: object,
confidence: object?._meta.confidence_score,
fieldConfidence: object?._meta.field_confidence,
credits: metadata.credits,
verification: verification,
});
}
