Skip to main content

Installation

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:
PARSEFY_API_KEY=pk_your_api_key

Server Action

Create a server action to handle document extraction:
// 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:
// 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:
// 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,
  });
}