Getting started

github.com/Serendipity-AI/markdown2pdf-typescript

Our Typescript SDK makes it easy to use markdown2pdf.ai in your agentic workflows. Whilst using our REST APIs is straightforward, there are some complexities (such as polling for payment completion and document generation) that the SDK handles for you.

First, install the SDK:

npm install @serendipityai/markdown2pdf-typescript

If you run your TypeScript or ES module file (for example, via node test.ts) and see a warning like Module type of file is not specified and it doesn’t parse as CommonJS, you can add a "type": "module" (or "type": "commonjs") in your package.json (or rename your file to .mjs) to remove the warning.

With that done, you can try a sample code snippet:

import { convertMarkdownToPdf } from "@serendipityai/markdown2pdf-typescript";
import type { OfferDetails } from "@serendipityai/markdown2pdf-typescript";

async function pay(offer: OfferDetails) {
    console.log("⚡ Lightning payment required");
    console.log(`Amount: ${offer.amount} ${offer.currency}`);
    console.log(`Description: ${offer.description}`);
    console.log(`Invoice: ${offer.payment_request}`);
    console.log("Press ENTER after completing the payment to continue...");
    await new Promise< void >(resolve => { process.stdin.once("data", () => { resolve(); }); });
}

async function main() {
    const result = await convertMarkdownToPdf("# Hello from Typescript", {
        title: "My document title",
        downloadPath: "output.pdf",
        onPaymentRequest: pay
    });
    console.log("Saved PDF to:", result);
}

main().catch(console.error);

This code converts your markdown into a PDF. If a Lightning payment is required, the SDK calls your onPaymentRequest callback (here our pay function) so that you can handle the payment (for example, by printing the invoice and waiting for manual confirmation). You can also integrate with services like fewsats.com or albyhub for automatic payments.


SDK Reference

Types

The SDK exports the following types (and classes) (from “@serendipityai/markdown2pdf-typescript”):

OfferDetails
type

A TypeScript type that describes the payment offer details. This type is used in the onPaymentRequest callback.

type OfferDetails = {
    offer_id: string;        // Unique identifier for the offer
    amount: number;          // The payment amount
    currency: string;        // The currency code (e.g., "usd")
    description: string;     // Description of what the payment is for
    payment_context_token: string;  // Token used for payment verification
    payment_request_url: string;    // URL where the payment request can be viewed
    payment_request?: string;       // Optional Lightning payment request (BOLT11 invoice)
};

Example usage:

async function pay(offer: OfferDetails) {
    console.log(`Payment required: ${offer.amount} ${offer.currency}`);
    console.log(`Description: ${offer.description}`);
    if (offer.payment_request) {
        console.log(`Lightning invoice: ${offer.payment_request}`);
    }
    // ... handle payment ...
}
ConvertToPdfParams
type

A TypeScript type that describes the configuration options for the convertMarkdownToPdf function.

type ConvertToPdfParams = {
    onPaymentRequest?: (offer: OfferDetails) => Promise<void>;  // Callback for handling Lightning payments
    date?: string;          // Date to display on the cover page
    title?: string;         // Title to display on the cover page
    downloadPath?: string;  // Path where the PDF should be saved
    returnBytes?: boolean;  // Whether to return the PDF as a Buffer instead of saving to file
    apiUrl?: string;        // Optional override for the API URL (for development/testing)
};

Example usage:

const options: ConvertToPdfParams = {
    title: "My Document",
    date: "2024-03-20",
    downloadPath: "output.pdf",
    onPaymentRequest: async (offer) => {
        console.log(`Payment required: ${offer.amount} ${offer.currency}`);
        // ... handle payment ...
    }
};

const result = await convertMarkdownToPdf("# Hello World", options);
  • Markdown2PdfError – A class (extending Error) thrown (or used) for general conversion errors.

  • PaymentRequiredError – A class (extending Markdown2PdfError) thrown (or used) if a payment is required but no “onPaymentRequest” callback is provided.


Function: convertMarkdownToPdf

Signature:

async function convertMarkdownToPdf(
    markdown: string,
    options?: ConvertToPdfParams
): Promise<string | Buffer>

Description:

Converts the provided markdown (a string) into a PDF. The function takes two parameters:

  1. markdown: The markdown content to convert
  2. options: An optional object containing configuration parameters for the conversion process

If a Lightning payment is required, the SDK calls your onPaymentRequest callback so that you can handle the payment. If onPaymentRequest is omitted and a payment is required, a PaymentRequiredError is thrown.
The SDK retrieves the final PDF; either saving it to downloadPath or returning it as a Buffer if returnBytes is true.

Parameters:

markdown
string
required

A string containing the markdown content you want to convert to PDF.

A note on markdown hashing

markdown2pdf.ai uses a hashing mechanism to ensure that you won’t get charged twice for creating the same PDF. Subsequent requests to convert the same markdown content will return the previously generated PDF without requiring a new payment.

For example:

const result = await convertMarkdownToPdf("# Hello from Typescript", {
    title: "My document title",
    downloadPath: "output.pdf"
});
options.onPaymentRequest
function
required

A callback function that is called when a Lightning payment is required. The function receives an offer object containing payment details. This parameter is required because the service uses the L402 protocol, which requires a payment to be made before generating the PDF.

async function pay(offer: OfferDetails) {
    console.log("⚡ Lightning payment required");
    console.log(`Amount: ${offer.amount} ${offer.currency}`);
    console.log(`Description: ${offer.description}`);
    console.log(`Invoice: ${offer.payment_request}`);
    await new Promise<void>(resolve => { process.stdin.once("data", () => { resolve(); }); });
}

const result = await convertMarkdownToPdf("# Hello from Typescript", {
    onPaymentRequest: pay,
    downloadPath: "output.pdf"
});
offer = {
    offer_id: "abc",
    amount: 1,
    currency: "usd",
    description: "test payment",
    payment_context_token: "def",
    payment_request_url: "http://path.to/payment/request",
    payment_request: "lnbc..." // Optional
}
options.date
string

A string containing the date to use on the front cover. If not provided, the current date will be used.

For example:

const result = await convertMarkdownToPdf("# Hello from Typescript", {
    date: "5th June 2025",
    downloadPath: "output.pdf"
});
options.title
string

A string containing the title to use on the front cover. If not provided, a default will be used.

For example:

const result = await convertMarkdownToPdf("# Hello from Typescript", {
    title: "My document title",
    downloadPath: "output.pdf"
});
options.downloadPath
string

A string containing the path to use for saving the PDF file. If not provided and returnBytes is false, the PDF URL will be returned instead.

For example:

const path = await convertMarkdownToPdf("# Save this one", {
    downloadPath: "output.pdf"
});
console.log("Saved PDF to:", path);
options.returnBytes
boolean

Whether or not to return the PDF as bytes instead of saving it to a file. If set to true, the method will return the PDF content as a Buffer. If set to false, it will save the PDF to the specified downloadPath or return the URL if no path is provided.

For example:

const pdfBytes = await convertMarkdownToPdf("# Memory use case", {
    returnBytes: true
});
console.log(`PDF size in memory: ${pdfBytes.length} bytes`);
options.apiUrl
string

This parameter enables you to override the default API URL. This is typically used in development or testing environments, but for normal usage you won’t need to touch it and it can be set to undefined or omitted.

Returns

ConditionResolves toType
downloadPath is provided and returnBytes is falseThe value of downloadPathstring
returnBytes is trueThe PDF as a BufferBuffer
Neither downloadPath nor returnBytes is provided/trueThe final download URLstring
An error occurs (network, timeout, or payment required and no callback)Rejects with errorMarkdown2PdfError or PaymentRequiredError

The function always returns a Promise that resolves or rejects as described above.


Automating payments

If you want to automate Lightning payments (so that your “onPaymentRequest” callback pays the invoice automatically), you can integrate with a service (or Lightning wallet) such as fewsats.com or albyhub. (These services (or wallets) allow you to pay Lightning invoices “headlessly”.)
Below are two example integrations (using “fewsats” (via the Fewsats SDK) and “alby” (via axios) respectively):

import { convertMarkdownToPdf } from "@serendipityai/markdown2pdf-typescript";
import axios from 'axios';
import type { OfferDetails } from '@serendipityai/markdown2pdf-typescript';

// Configure Alby client
const ALBY_API_URL = 'https://api.getalby.com';
const client = axios.create({
  baseURL: ALBY_API_URL,
  headers: {
    'Authorization': `Bearer ${process.env.ALBY_ACCESS_TOKEN}`,
    'Content-Type': 'application/json'
  }
});

async function pay(offer: OfferDetails) {
  console.log("Paying using Alby:");
  try {
    // Pay the invoice using Alby's API
    const response = await client.post('/payments/bolt11', {
      invoice: offer.payment_request
    });

    if (response.status === 200)
      console.log('Payment successful:', response.data);
    else
      console.log('Payment status:', response.status);
  } catch (error) {
    if (axios.isAxiosError(error))
      console.error('Payment failed:', error.response?.data || error.message);
    else
      console.error('Payment failed:', error);
  }
  await new Promise(resolve => { process.stdin.once("data", () => resolve(undefined)); });
}

async function main() {
  const path = await convertMarkdownToPdf("# Save this one using Alby", { 
    downloadPath: "output.pdf", 
    onPaymentRequest: pay 
  });
  console.log("Saved PDF to:", path);
}

main().catch(console.error);