@usepapier/client
v0.2.1
Published
Official Papier SDK for TypeScript/JavaScript
Readme
@usepapier/client
The official TypeScript/JavaScript SDK for Papier - a type-safe template management platform for emails, SMS, documents, and notifications.
📦 Installation
npm install @usepapier/client🚀 Quick Start
1. Initialize with CLI
First, set up your project with the Papier CLI:
npx @usepapier/cli init
npx @usepapier/cli syncThis generates type-safe client code in .papier/index.ts.
2. Use the Generated Client
import { createPapierClient } from './.papier';
const papier = createPapierClient({
project: 'marketing',
apiKey: process.env.PAPIER_API_KEY,
environment: 'production'
});
// Send a welcome email with full type safety
await papier.email('welcome-email').send({
to: '[email protected]',
from: '[email protected]',
data: {
userName: 'John Doe',
verificationUrl: 'https://app.com/verify/...'
}
});📖 Core Concepts
Type-Safe Template Access
The CLI generates a fully typed client based on your template schemas:
// ✅ Type-safe: knows exactly what data this template needs
await papier.email('order-confirmation').send({
to: customer.email,
data: {
orderNumber: '12345',
items: [...],
total: 99.99
// TypeScript will error if you're missing required fields!
}
});Surface Types
Papier supports multiple communication surfaces:
- Email - HTML/text emails via adapters (SendGrid, etc.)
- SMS - Text messages
- Documents - PDF generation
- Notifications - Push notifications
🔧 Configuration
Basic Configuration
import { createPapierClient } from './.papier';
const papier = createPapierClient({
project: 'my-project', // Required: Project slug
apiKey: process.env.PAPIER_API_KEY, // Required: Your API key
environment: 'production', // Optional: production | staging | development
defaultLocale: 'en' // Optional: Default locale for templates
});Email Configuration
import { createPapierClient } from './.papier';
import { sendgridAdapter } from '@usepapier/sendgrid-adapter';
const papier = createPapierClient({
project: 'marketing',
apiKey: process.env.PAPIER_API_KEY,
emailConfig: {
from: {
name: 'Acme Corp',
email: '[email protected]'
},
adapters: [
sendgridAdapter({
apiKey: process.env.SENDGRID_API_KEY
})
]
}
});SMS Configuration
const papier = createPapierClient({
project: 'notifications',
apiKey: process.env.PAPIER_API_KEY,
smsConfig: {
from: '+1234567890',
adapters: [
twilioAdapter({
accountSid: process.env.TWILIO_ACCOUNT_SID,
authToken: process.env.TWILIO_AUTH_TOKEN
})
]
}
});📧 Email Templates
Basic Email Send
await papier.email('welcome-email').send({
to: '[email protected]',
data: {
userName: 'John',
// ... template variables
}
});Advanced Email Options
await papier.email('newsletter').send({
to: ['[email protected]', '[email protected]'], // Multiple recipients
from: {
name: 'Marketing Team',
email: '[email protected]'
},
subject: 'Custom subject override', // Override template subject
data: {
// Template variables with full type safety
articles: [...],
unsubscribeUrl: '...'
}
});Build Email Without Sending
const built = await papier.email('order-receipt').build({
to: '[email protected]',
data: {
orderNumber: '12345',
items: [...]
}
});
console.log(built.html); // Rendered HTML
console.log(built.text); // Rendered plain text
console.log(built.subject); // Rendered subject
console.log(built.metadata); // Template metadata
// Send later with custom logic
await myEmailService.send(built);📱 SMS Templates
Basic SMS Send
await papier.sms('verification-code').send({
to: '+1234567890',
data: {
code: '123456',
expiresIn: '10 minutes'
}
});Build SMS Without Sending
const built = await papier.sms('appointment-reminder').build({
to: '+1234567890',
data: {
appointmentTime: '2pm tomorrow',
doctorName: 'Dr. Smith'
}
});
console.log(built.text); // Rendered message text📄 Document Templates
Generate PDF Document
const doc = await papier.document('invoice').render({
data: {
invoiceNumber: 'INV-001',
client: {
name: 'Acme Corp',
address: '...'
},
items: [
{ description: 'Service', amount: 1000 }
],
total: 1000
}
});
console.log(doc.pdf); // Base64 encoded PDF
console.log(doc.metadata); // Template metadata
// Save to file
import fs from 'fs';
fs.writeFileSync('invoice.pdf', Buffer.from(doc.pdf, 'base64'));🔔 Notification Templates
Send Notification
await papier.notification('new-message').send({
data: {
senderName: 'Alice',
messagePreview: 'Hey, how are you?'
}
});🔌 Adapters
Email Adapters
SendGrid Adapter
import { sendgridAdapter } from '@usepapier/sendgrid-adapter';
const papier = createPapierClient({
project: 'marketing',
emailConfig: {
adapters: [
sendgridAdapter({
apiKey: process.env.SENDGRID_API_KEY
})
]
}
});Virtual Inbox Adapter (Testing)
import { virtualInboxAdapter } from '@usepapier/client';
const inbox = virtualInboxAdapter();
const papier = createPapierClient({
project: 'marketing',
emailConfig: {
adapters: [inbox]
}
});
await papier.email('test').send({ to: '[email protected]', data: {} });
console.log(inbox.messages); // Array of sent messages
inbox.clear(); // Clear inboxCustom Email Adapter
import type { EmailAdapter } from '@usepapier/client';
const customAdapter: EmailAdapter = {
name: 'my-custom-adapter',
send: async (input) => {
// input.html - Rendered HTML
// input.text - Rendered text
// input.to - Recipient(s)
// input.from - Sender
// input.subject - Subject line
// input.data - Template variables
const response = await myEmailService.send({
to: input.to,
from: input.from,
subject: input.subject,
html: input.html
});
return {
id: response.messageId,
provider: 'my-service'
};
}
};SMS Adapters
import type { SMSAdapter } from '@usepapier/client';
const customSMSAdapter: SMSAdapter = {
name: 'my-sms-adapter',
send: async (input) => {
// input.text - Rendered message text
// input.to - Recipient phone number
// input.from - Sender phone number
// input.data - Template variables
const response = await mySMSService.send({
to: input.to,
from: input.from,
body: input.text
});
return {
id: response.messageId,
provider: 'my-sms-service'
};
}
};🎯 Advanced Features
Dynamic Adapter Routing
Route different emails to different adapters based on conditions:
const papier = createPapierClient({
project: 'marketing',
emailConfig: {
adapters: [sendgridAdapter, postmarkAdapter],
routing: ({ templateSlug, to }) => {
// Use Postmark for transactional emails
if (templateSlug.includes('transaction')) {
return 'postmark';
}
// Use SendGrid for marketing
return 'sendgrid';
}
}
});Schema Validation Error Handling
const papier = createPapierClient({
project: 'marketing',
apiKey: process.env.PAPIER_API_KEY,
onSchemaMismatch: {
email: async (error, context) => {
console.error('Schema validation failed:', error);
console.log('Template:', context.templateName);
console.log('Variables:', context.variables);
// Option 1: Send to a fallback queue
await context.sendToResendQueue();
// Option 2: Log to monitoring service
await myMonitoring.logError(error, context);
}
}
});Multi-Project Support
// Create separate clients for different projects
const marketingPapier = createPapierClient({
project: 'marketing',
apiKey: process.env.PAPIER_API_KEY
});
const transactionalPapier = createPapierClient({
project: 'transactional',
apiKey: process.env.PAPIER_API_KEY
});
await marketingPapier.email('newsletter').send({ ... });
await transactionalPapier.email('password-reset').send({ ... });Locale Support
const papier = createPapierClient({
project: 'marketing',
defaultLocale: 'en'
});
// Use specific locale for a template
await papier.email('welcome-email').send({
to: '[email protected]',
data: {
locale: 'es', // Spanish version of template
userName: 'Juan'
}
});🧪 Testing
Using Virtual Inbox
import { virtualInboxAdapter } from '@usepapier/client';
describe('Email notifications', () => {
it('sends welcome email', async () => {
const inbox = virtualInboxAdapter();
const papier = createPapierClient({
project: 'marketing',
emailConfig: { adapters: [inbox] }
});
await papier.email('welcome-email').send({
to: '[email protected]',
data: { userName: 'Test User' }
});
expect(inbox.messages).toHaveLength(1);
expect(inbox.messages[0].to).toBe('[email protected]');
expect(inbox.messages[0].html).toContain('Test User');
});
});Build Without Sending
it('renders correct content', async () => {
const built = await papier.email('order-confirmation').build({
to: '[email protected]',
data: {
orderNumber: '12345',
total: 99.99
}
});
expect(built.html).toContain('Order #12345');
expect(built.html).toContain('$99.99');
expect(built.subject).toBe('Your order #12345 is confirmed');
});🏗️ TypeScript Support
This SDK is built with TypeScript and provides full type safety:
// ✅ TypeScript knows the exact shape of template data
await papier.email('welcome-email').send({
to: '[email protected]',
data: {
userName: 'John',
// ✅ Autocomplete for all required fields
// ❌ TypeScript error if fields are missing or wrong type
}
});
// ✅ Return types are fully typed
const built = await papier.email('newsletter').build({ ... });
// built.html: string
// built.text: string
// built.subject: string
// built.metadata: TemplateMetadata📚 API Reference
createPapierClient(options)
Creates a new Papier client instance.
Options:
project(string, required) - Project slugapiKey(string) - API key (can usePAPIER_API_KEYenv var)environment(string) - Environment:production,staging,developmentdefaultLocale(string) - Default locale code (e.g.,en,es,fr)emailConfig(object) - Email configurationfrom- Default senderadapters- Array of email adaptersrouting- Adapter routing function
smsConfig(object) - SMS configurationfrom- Default sender phoneadapters- Array of SMS adaptersrouting- Adapter routing function
Email Methods
// Get email surface for a template
papier.email(templateSlug: string): EmailSurface
// Send email
emailSurface.send(options): Promise<{ id?: string, provider?: string }>
// Build email without sending
emailSurface.build(options): Promise<BuiltEmailPayload>SMS Methods
// Get SMS surface for a template
papier.sms(templateSlug: string): SmsSurface
// Send SMS
smsSurface.send(options): Promise<{ id?: string, provider?: string }>
// Build SMS without sending
smsSurface.build(options): Promise<BuiltSmsPayload>Document Methods
// Get document surface for a template
papier.document(templateSlug: string): DocumentSurface
// Render document
documentSurface.render(options): Promise<{ pdf: string, metadata: TemplateMetadata }>Notification Methods
// Get notification surface for a template
papier.notification(templateSlug: string): NotificationSurface
// Send notification
notificationSurface.send(options): Promise<{ id?: string, provider?: string }>🌍 Environment Variables
PAPIER_API_KEY=your-api-key # API authentication key
PAPIER_ENV=production # Environment (production/staging/development)
SDK_PAPIER_API_URL=https://... # Custom API URL (optional)🔗 Related Packages
@usepapier/cli- CLI for template management and code generation@usepapier/sendgrid-adapter- SendGrid email adapter@usepapier/twilio-adapter- Twilio SMS adapter
📄 License
MIT
🤝 Contributing
Issues and pull requests welcome!
💬 Support
- Documentation: https://docs.usepapier.com
- Issues: https://github.com/yourusername/papier-monorepo/issues
Made with ❤️ by the Papier team
