npm package: @papyrify/react
Description: Official React SDK for Papyrify PDF Generation
Author: Papyrify
Latest version: 1.0.0

# @papyrify/react

Official React SDK for Papyrify PDF Generation

## Installation

```bash
npm install @papyrify/react
# or
yarn add @papyrify/react
# or
pnpm add @papyrify/react
```

## Getting Your API Key

1. Log in to your Papyrify Dashboard
2. Go to **API Tokens** (left sidebar)
3. Click **Create Token**
4. Choose a name, select scopes (at minimum `pdf:generate`), and set access scope (ALL, FOLDER, or DOCUMENT)
5. Copy your token (starts with `pf_live_`)
6. Store it securely in your environment variables

**Important Security Notes:**
- Never commit API tokens to version control
- Use environment variables (`.env.local` files)
- Tokens are shown only once at creation — save them immediately
- You can revoke tokens at any time from the dashboard

```env
# .env.local
NEXT_PUBLIC_PAPYRIFY_API_KEY=pf_live_your_token_here
```

## Quick Start

```tsx
import { usePapyrify } from '@papyrify/react'

function QuoteGenerator() {
  const { generatePDF, loading, error } = usePapyrify({
    apiKey: process.env.NEXT_PUBLIC_PAPYRIFY_API_KEY
  })

  const handleGenerate = async () => {
    try {
      const result = await generatePDF({
        templateId: 'your_template_id', // from Papyrify studio
        data: {
          clientName: 'John Doe',
          amount: 1500,
        }
      })

      // PDF is ready!
      window.open(result.downloadUrl, '_blank')
    } catch (error) {
      console.error('Failed to generate PDF:', error)
    }
  }

  return (
    <button onClick={handleGenerate} disabled={loading}>
      {loading ? 'Generating...' : 'Generate PDF'}
    </button>
  )
}
```

## Usage with Provider

For apps that need global configuration:

```tsx
import { PapyrifyProvider, usePapyrify } from '@papyrify/react'

// Wrap your app with the provider
function App() {
  return (
    <PapyrifyProvider
      config={{
        apiKey: process.env.NEXT_PUBLIC_PAPYRIFY_API_KEY,
      }}
    >
      <YourApp />
    </PapyrifyProvider>
  )
}

// Use the hook anywhere — config is inherited from provider
function QuoteGenerator() {
  const { generatePDF, loading } = usePapyrify()

  const handleGenerate = async () => {
    const result = await generatePDF({
      templateId: 'quote_123',
      data: { clientName: 'John Doe' }
    })
  }

  return <button onClick={handleGenerate}>Generate</button>
}
```

## API Reference

### `usePapyrify(config?)`

React hook for PDF generation.

**Parameters:**
- `config` (optional): Configuration object
  - `apiKey` (string): Your Papyrify API token (`pf_live_...`)

**Returns:**
- `generatePDF(options)`: Function to generate PDF
- `loading` (boolean): Loading state
- `error` (Error | null): Error object if generation failed
- `result` (GenerateResult | null): Last generation result

### `generatePDF(options)`

Generate a PDF from template and data.

**Parameters:**
- `templateId` (string, required): Document ID from Papyrify Studio
- `data` (object, required): Key-value pairs matching your template's variable names
- `format` (optional): `'base64'` (default) or `'blob'`
- `filename` (optional): Custom filename for the PDF

**Returns:** `Promise<GenerateResult>`

```typescript
interface GenerateResult {
  success: boolean
  documentId: string
  pdfUrl?: string        // data: URL (base64 mode)
  pdfBase64?: string     // raw base64 string
  downloadUrl: string    // URL to open/download the PDF
  metadata: {
    pages: number
    size: string         // e.g. "45.12 KB"
    generatedAt: string  // ISO date
    templateId: string
  }
}
```

### Token Scopes

When creating an API token, you can assign these scopes:

| Scope | Description |
|-------|-------------|
| `pdf:generate` | **Required** — Generate PDF documents |
| `documents:read` | View document templates |
| `documents:write` | Create and update documents |
| `documents:delete` | Delete documents |
| `organization:read` | View organization details |
| `analytics:read` | View analytics data |

### Token Access Scope

Control which documents a token can access:

| Scope Type | Description |
|-----------|-------------|
| `ALL` | Access all documents in your organization |
| `FOLDER` | Access only documents in a specific folder |
| `DOCUMENT` | Access a single specific document |

## Complete Example

```tsx
import { usePapyrify } from '@papyrify/react'
import { useState } from 'react'

function InvoiceGenerator() {
  const [clientName, setClientName] = useState('')
  const [amount, setAmount] = useState(0)

  const { generatePDF, loading, error } = usePapyrify({
    apiKey: process.env.NEXT_PUBLIC_PAPYRIFY_API_KEY,
  })

  const handleSubmit = async (e: React.FormEvent) => {
    e.preventDefault()

    try {
      const result = await generatePDF({
        templateId: 'invoice_template_id',
        data: {
          clientName,
          amount,
          date: new Date().toLocaleDateString(),
          invoiceNumber: `INV-${Date.now()}`
        }
      })

      // Option 1: Open in new tab
      window.open(result.downloadUrl, '_blank')

      // Option 2: Download directly
      // const link = document.createElement('a')
      // link.href = result.downloadUrl
      // link.download = 'invoice.pdf'
      // link.click()

    } catch (err) {
      console.error('Generation failed:', err)
    }
  }

  return (
    <form onSubmit={handleSubmit}>
      <input
        value={clientName}
        onChange={e => setClientName(e.target.value)}
        placeholder="Client name"
      />
      <input
        type="number"
        value={amount}
        onChange={e => setAmount(Number(e.target.value))}
        placeholder="Amount"
      />
      <button type="submit" disabled={loading}>
        {loading ? 'Generating...' : 'Generate Invoice PDF'}
      </button>
      {error && <p style={{ color: 'red' }}>{error.message}</p>}
    </form>
  )
}
```

## Blob Format (Direct Download)

For direct file downloads without base64:

```tsx
const result = await generatePDF({
  templateId: 'template_id',
  data: { clientName: 'John Doe' },
  format: 'blob'  // Returns raw PDF binary
})

// result.downloadUrl is an Object URL — download it
const link = document.createElement('a')
link.href = result.downloadUrl
link.download = 'document.pdf'
link.click()

// Clean up the Object URL when done
URL.revokeObjectURL(result.downloadUrl)
```

## Error Handling

```tsx
const { generatePDF, error } = usePapyrify({
  apiKey: process.env.NEXT_PUBLIC_PAPYRIFY_API_KEY
})

const handleGenerate = async () => {
  try {
    const result = await generatePDF(options)
  } catch (error) {
    if (error.message.includes('Authentication required')) {
      console.error('Invalid or missing API key')
    } else if (error.message.includes('not found')) {
      console.error('Template not found — check your templateId')
    } else if (error.message.includes('missing required scopes')) {
      console.error('Token needs pdf:generate scope')
    } else if (error.message.includes('does not have access')) {
      console.error('Token scope does not cover this document')
    } else {
      console.error('Unknown error:', error)
    }
  }
}
```

## TypeScript Support

Full TypeScript support out of the box:

```typescript
import type { GenerateOptions, GenerateResult, PapyrifyConfig } from '@papyrify/react'

const config: PapyrifyConfig = {
  apiKey: 'pf_live_...',
}

const options: GenerateOptions = {
  templateId: 'quote_123',
  data: { clientName: 'John Doe', amount: 1500 }
}
```

## License

MIT