All Docs
FeaturesCalmony PayUpdated March 15, 2026

Fix: invoices.downloadPdf() now returns PDF bytes instead of throwing

Fix: invoices.downloadPdf() now returns PDF bytes instead of throwing

Release: v1.0.92

Background

Calmony Pay's SDK exposes an invoices.downloadPdf(id) method intended to retrieve a generated PDF invoice by its ID. Internally, the server-side route GET /v1/invoices/:id/pdf serves the PDF as raw binary data with a Content-Type: application/pdf response header.

What was broken

Prior to v1.0.92, invoices.downloadPdf(id) was implemented by delegating to the SDK's shared request() helper:

// Old implementation (broken)
downloadPdf(id: string) {
  return this.request<object>("GET", `/v1/invoices/${id}/pdf`);
}

The request() helper always calls response.json() to parse the server's reply. Because the /pdf route returns binary bytes — not JSON — this produced a runtime exception every time the method was called:

SyntaxError: Unexpected token '%', "\x25PDF-1."... is not valid JSON

The method was effectively unusable.

What changed

The invoices.downloadPdf(id) method now issues its HTTP request directly, skipping the shared request() helper entirely, and resolves to an ArrayBuffer containing the raw PDF bytes:

// New implementation (correct)
async downloadPdf(id: string): Promise<ArrayBuffer> {
  const response = await fetch(`/v1/invoices/${id}/pdf`);
  return response.arrayBuffer();
}

The server-side route GET /v1/invoices/:id/pdf is unchanged — it already returned the correct binary PDF content. Only the client SDK method needed fixing.

How to use it

Node.js — write PDF to disk

import { writeFile } from 'fs/promises';
import { calmonyPay } from '@/lib/calmony-pay/client';

const buffer = await calmonyPay.invoices.downloadPdf('inv_abc123');
await writeFile('invoice.pdf', Buffer.from(buffer));

Browser — trigger a download

import { calmonyPay } from '@/lib/calmony-pay/client';

const buffer = await calmonyPay.invoices.downloadPdf('inv_abc123');
const blob = new Blob([buffer], { type: 'application/pdf' });
const url = URL.createObjectURL(blob);

const a = document.createElement('a');
a.href = url;
a.download = 'invoice.pdf';
a.click();
URL.revokeObjectURL(url);

Next.js API route — proxy PDF to a browser client

// app/api/invoice-download/route.ts
import { calmonyPay } from '@/lib/calmony-pay/client';
import { NextRequest, NextResponse } from 'next/server';

export async function GET(req: NextRequest) {
  const id = req.nextUrl.searchParams.get('id')!;
  const buffer = await calmonyPay.invoices.downloadPdf(id);

  return new NextResponse(buffer, {
    headers: {
      'Content-Type': 'application/pdf',
      'Content-Disposition': `attachment; filename="invoice-${id}.pdf"`,
    },
  });
}

Affected files

FileChange
src/lib/calmony-pay/client.tsinvoices.downloadPdf() bypasses request() helper; returns ArrayBuffer
src/app/api/v1/invoices/[id]/pdf/route.tsNo change — already correct