Developer Experience

One API Call. PDF + Excel.

Stop juggling Puppeteer, PDFKit, and ExcelJS. Generate documents in minutes, not weeks.

npm i @rynko/sdkpip install rynkodev.rynko:sdk

Delete 90% of your document code

Compare real-world implementations. No hidden helper functions or abstractions.

Traditional Approach
~162 lines
Multiple services, template files, error handling, and configuration
// invoice-pdf-service.ts - Puppeteer + Handlebars
// Full implementation for generating PDF invoices

import puppeteer, { Browser } from 'puppeteer';
import Handlebars from 'handlebars';
import * as fs from 'fs';
import * as path from 'path';

// ============================================
// Type Definitions
// ============================================
interface InvoiceItem {
  description: string;
  quantity: number;
  unitPrice: number;
  total: number;
}

interface InvoiceData {
  invoiceNumber: string;
  customerName: string;
  customerEmail: string;
  companyName: string;
  companyAddress: string;
  items: InvoiceItem[];
  subtotal: number;
  tax: number;
  total: number;
  dueDate: string;
  issueDate: string;
}

// ============================================
// Puppeteer Browser Management
// ============================================
let browser: Browser | null = null;

async function getBrowser(): Promise<Browser> {
  if (!browser) {
    browser = await puppeteer.launch({
      headless: true,
      args: [
        '--no-sandbox',
        '--disable-setuid-sandbox',
        '--disable-dev-shm-usage',
        '--disable-gpu',
      ],
    });
  }
  return browser;
}

// ============================================
// Template Loading & Compilation
// ============================================
const pdfTemplatePath = path.join(__dirname, 'templates', 'invoice-pdf.hbs');
let pdfTemplate: Handlebars.TemplateDelegate;

function loadTemplates() {
  pdfTemplate = Handlebars.compile(
    fs.readFileSync(pdfTemplatePath, 'utf-8')
  );
}

// Register Handlebars helpers for formatting
Handlebars.registerHelper('formatCurrency', (value: number) => {
  return new Intl.NumberFormat('en-US', {
    style: 'currency',
    currency: 'USD',
  }).format(value);
});

Handlebars.registerHelper('formatDate', (date: string) => {
  return new Date(date).toLocaleDateString('en-US', {
    year: 'numeric',
    month: 'long',
    day: 'numeric',
  });
});

Handlebars.registerHelper('multiply', (a: number, b: number) => a * b);

// Load templates on module init
loadTemplates();

// ============================================
// PDF Generation with Puppeteer
// ============================================
export async function generateInvoicePDF(data: InvoiceData): Promise<Buffer> {
  const browser = await getBrowser();
  const page = await browser.newPage();

  try {
    // Render HTML from Handlebars template
    const html = pdfTemplate(data);

    // Set viewport for consistent rendering
    await page.setViewport({ width: 794, height: 1123 }); // A4 size

    // Set content and wait for resources
    await page.setContent(html, {
      waitUntil: 'networkidle0',
      timeout: 30000,
    });

    // Add custom styles for print
    await page.addStyleTag({
      content: `
        @page { margin: 0; }
        body { margin: 20mm; }
      `,
    });

    // Generate PDF buffer
    const pdfBuffer = await page.pdf({
      format: 'A4',
      printBackground: true,
      preferCSSPageSize: true,
      margin: {
        top: '20mm',
        right: '20mm',
        bottom: '20mm',
        left: '20mm',
      },
    });

    return Buffer.from(pdfBuffer);
  } catch (error) {
    throw new Error(`PDF generation failed: ${(error as Error).message}`);
  } finally {
    await page.close();
  }
}

// ============================================
// Save PDF to file or upload to S3
// ============================================
export async function saveInvoicePDF(data: InvoiceData): Promise<string> {
  try {
    if (!data.invoiceNumber) {
      throw new Error('Missing required invoice data');
    }

    console.log(`Generating PDF for invoice ${data.invoiceNumber}...`);
    const pdfBuffer = await generateInvoicePDF(data);

    // Save to file or upload to S3
    const filename = `invoice-${data.invoiceNumber}.pdf`;
    fs.writeFileSync(path.join(__dirname, 'output', filename), pdfBuffer);

    console.log(`Invoice PDF saved: ${filename}`);
    return filename;
  } catch (error) {
    console.error('Failed to generate invoice:', error);
    throw new Error(`PDF generation failed: ${(error as Error).message}`);
  }
}

// Cleanup on process exit
process.on('exit', async () => {
  if (browser) await browser.close();
});

+ Not shown: Handlebars template file (~100 lines), environment setup, dependency management, browser/service initialization, and production deployment configuration.

The Rynko Way
~43 lines
One API call. PDF or Excel from the same template.
// invoice-pdf-service.ts - Rynko
// Generate invoice PDF with one API call

// Types
interface InvoiceData {
  invoiceNumber: string;
  customerName: string;
  items: Array<{
    description: string;
    quantity: number;
    unitPrice: number;
  }>;
  subtotal: number;
  tax: number;
  total: number;
  dueDate: string;
}

// Generate PDF - that's it!
export async function generateInvoicePDF(data: InvoiceData) {
  // Step 1: Queue the document
  const response = await fetch('https://api.rynko.dev/api/v1/documents/generate', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${process.env.RYNKO_API_KEY}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      templateId: 'invoice-template',
      format: 'pdf',
      variables: data,
    }),
  });

  const { jobId } = await response.json();

  // Step 2: Poll for completion (or use webhooks)
  const job = await pollForCompletion(jobId);
  return { jobId, downloadUrl: job.downloadUrl };
}

// No Handlebars. No Puppeteer. No DocRaptor.
// Just your data + one API call + webhook or polling.
Lines of code:
162+ lines43 lines
External services:
2-3 services1 API
Template files:
1-2 files0 files
Code reduction:73%

Get Started in 5 Minutes

Three steps to generating your first document.

1
Get API Key
Sign up and grab your API key from the dashboard. Takes 30 seconds.
2
Design Templates
Use the visual editor or generate templates with AI. Define your variables.
3
Generate via API
POST your data to our API. We generate the document and return a download URL.
Sample API Call
curl -X POST https://api.rynko.dev/api/v1/documents/generate \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "templateId": "invoice-template",
    "format": "pdf",
    "variables": {
      "invoiceNumber": "INV-001",
      "items": [{ "name": "Product", "quantity": 2, "price": 49.99 }],
      "total": 99.98
    }
  }'

# Response (202 Accepted):
# { "jobId": "doc_abc123", "status": "queued", "statusUrl": "...", "estimatedWaitSeconds": 5 }

# Poll GET /v1/documents/jobs/doc_abc123 until completed:
# { "jobId": "doc_abc123", "status": "completed", "downloadUrl": "..." }

Built for Developers

Everything you need for a smooth integration.

OpenAPI Spec
Full API documentation with Swagger UI
Async with Webhooks
Get notified when documents are ready
Official SDKs
API Playground
Test API calls directly in the dashboard
Bring Your Own Visuals

Your Stack, Our Layout

Use Python, R, Go, or Node. Generate your Visual as SVGs on your stack, send them to us, and we convert them to crisp PNGs for reliable cross-platform rendering.

Py
Python
matplotlib, plotly, seaborn, altair
R
R
ggplot2, plotly, lattice
Go
Go
go-chart, gonum/plot, SVGo
JS
Node.js
D3.js, sharp, jsbarcode
Embed Custom Charts in Documents

Generate SVG charts, barcodes, or diagrams with your favorite tools. Pass them as variables to your templates. We sanitize the SVG for security and embed it perfectly in your PDF or Excel file.

  • Works with any SVG-producing library
  • Automatic security sanitization
  • Base64 encoding for complex SVGs
  • Reference assets from your library
# Python example
import matplotlib.pyplot as plt
import io, base64

fig, ax = plt.subplots()
ax.bar(['Q1', 'Q2', 'Q3', 'Q4'], [10, 25, 15, 30])

buf = io.BytesIO()
fig.savefig(buf, format='svg')
svg_b64 = base64.b64encode(buf.getvalue()).decode()

# Pass to Rynko
client.documents.generate(
    template_id="quarterly-report",
    variables={"chartSvg": svg_b64}
)

Multiple Output Formats

Same template, different formats. Choose what works for your use case.

PDF
High-quality PDFs for invoices, reports, certificates, and contracts.
Excel
Excel files with formulas, formatting, and multiple sheets.
AI-Native

Generate Templates with AI

Feed our JSON schema to ChatGPT, Claude, or Gemini. Generate complex templates with calculated fields, conditionals, and loops in seconds.

Describe Your Template

"Create an invoice PDF with line items, tax calculation, and company logo"

AI Generates JSON

LLM outputs valid Rynko template schema with all components

Import & Refine

Paste JSON into the designer. Tweak visually. Done.

Ready to ship?

20 free documents per month. Full API access. No credit card required.