> ## Documentation Index
> Fetch the complete documentation index at: https://markdown2pdf.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Typescript SDK

> Easily use markdown2pdf.ai to convert markdown (.md) to high-quality and impactful PDF in your Typescript-based agentic workflows using our Typescript software development kit (SDK).

# Getting started

<Icon icon="github" iconType="solid" size={20} /> [github.com/Serendipity-AI/markdown2pdf-typescript](https://github.com/Serendipity-AI/markdown2pdf-typescript)

Our [Typescript SDK](https://github.com/Serendipity-AI/markdown2pdf-typescript) 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:

```bash theme={null}
npm install @serendipityai/markdown2pdf-typescript
```

<Tip>
  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.
</Tip>

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

```typescript theme={null}
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](https://fewsats.com) or [albyhub](https://albyhub.com) for automatic payments.

***

# SDK Reference

## Types

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

<ParamField body="OfferDetails" type="type">
  A TypeScript type that describes the payment offer details. This type is used in the `onPaymentRequest` callback.

  ```typescript theme={null}
  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:

  ```typescript theme={null}
  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 ...
  }
  ```
</ParamField>

<ParamField body="ConvertToPdfParams" type="type">
  A TypeScript type that describes the configuration options for the `convertMarkdownToPdf` function.

  ```typescript theme={null}
  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:

  ```typescript theme={null}
  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);
  ```
</ParamField>

* **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:**

```typescript theme={null}
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:**

<ParamField body="markdown" type="string" required>
  A string containing the markdown content you want to convert to PDF.

  <Tip>
    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.
  </Tip>

  For example:

  ```typescript theme={null}
  const result = await convertMarkdownToPdf("# Hello from Typescript", {
      title: "My document title",
      downloadPath: "output.pdf"
  });
  ```
</ParamField>

<ParamField body="options.onPaymentRequest" type="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.

  ```typescript theme={null}
  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"
  });
  ```

  ```typescript theme={null}
  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
  }
  ```
</ParamField>

<ParamField body="options.date" type="string">
  A string containing the date to use on the front cover. If not provided, the current date will be used.

  For example:

  ```typescript theme={null}
  const result = await convertMarkdownToPdf("# Hello from Typescript", {
      date: "5th June 2025",
      downloadPath: "output.pdf"
  });
  ```
</ParamField>

<ParamField body="options.title" type="string">
  A string containing the title to use on the front cover. If not provided, a default will be used.

  For example:

  ```typescript theme={null}
  const result = await convertMarkdownToPdf("# Hello from Typescript", {
      title: "My document title",
      downloadPath: "output.pdf"
  });
  ```
</ParamField>

<ParamField body="options.downloadPath" type="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:

  ```typescript theme={null}
  const path = await convertMarkdownToPdf("# Save this one", {
      downloadPath: "output.pdf"
  });
  console.log("Saved PDF to:", path);
  ```
</ParamField>

<ParamField body="options.returnBytes" type="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:

  ```typescript theme={null}
  const pdfBytes = await convertMarkdownToPdf("# Memory use case", {
      returnBytes: true
  });
  console.log(`PDF size in memory: ${pdfBytes.length} bytes`);
  ```
</ParamField>

<ParamField body="options.apiUrl" type="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.
</ParamField>

**Returns**

| Condition                                                               | Resolves to                 | Type                                          |
| ----------------------------------------------------------------------- | --------------------------- | --------------------------------------------- |
| `downloadPath` is provided and `returnBytes` is `false`                 | The value of `downloadPath` | string                                        |
| `returnBytes` is `true`                                                 | The PDF as a Buffer         | Buffer                                        |
| Neither `downloadPath` nor `returnBytes` is provided/true               | The final download URL      | string                                        |
| An error occurs (network, timeout, or payment required and no callback) | Rejects with error          | `Markdown2PdfError` 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](https://fewsats.com) or [albyhub](https://albyhub.com). For X402 (USDC on Solana) use any Solana wallet that holds USDC — see the X402 tab below.

<Tabs>
  <Tab title="Alby">
    ```typescript theme={null}
    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); 
    ```
  </Tab>

  <Tab title="lnbits">
    ```typescript theme={null}
    import { convertMarkdownToPdf } from "@serendipityai/markdown2pdf-typescript";
    import axios from "axios";
    import type { OfferDetails } from '@serendipityai/markdown2pdf-typescript';

    // Constants
    const LN_BITS_URL = "https://demo.lnbits.com/api/v1/payments";
    const ADMIN_KEY = process.env.LNBITS_ADMIN_KEY as string; // Set in your env

    async function pay(offer: OfferDetails) {
    console.log("Paying using lnbits:");

    try {
      const response = await axios.post(
        LN_BITS_URL,
        {
          out: true,
          bolt11: offer.payment_request
        },
        {
          headers: {
            "X-Api-Key": ADMIN_KEY,
            "Content-Type": "application/json"
          }
        }
      );

      console.log("Payment successful:", response.data);
    } catch (error) {
      if (axios.isAxiosError(error)) {
        console.error("Payment failed:", error.response?.data || error.message);
      } else {
        console.error("Unexpected error:", error);
      }
    }
    }

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

    main().catch(console.error);
    ```
  </Tab>

  <Tab title="Fewsats">
    ```typescript theme={null}
    import { convertMarkdownToPdf } from "@serendipityai/markdown2pdf-typescript";
    import type { OfferDetails } from '@serendipityai/markdown2pdf-typescript';
    import { Fewsats } from 'fewsats';
    // Configure the SDK
    const client = new Fewsats({ apiKey: process.env.FEWSATS_API_KEY });

    async function pay(offer: OfferDetails) {
     console.log("Paying using Fewsats:");
     console.log(offer);
     
     try {
       // Use the SDK to pay the offer
       const response = await client.payLightning(offer.payment_request, offer.amount, offer.currency, offer.description);

       if (response.success) {
         console.log('Payment successful:', response);
       } else {
         console.log('Payment failed:', response.error);
       }
     } catch (error) {
       console.error('Payment failed:', error.message);
     }

     await new Promise(resolve => { process.stdin.once("data", () => resolve(undefined)); });
    }

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

    main().catch(console.error);
    ```
  </Tab>

  <Tab title="Coinbase X402">
    ```typescript theme={null}
    import axios from "axios";
    import { withPaymentInterceptor } from "x402-axios";
    import { Keypair } from "@solana/web3.js";
    import { createKeypairSignerFromBytes } from "@solana/signers";
    import bs58 from "bs58";

    // Any Solana wallet that holds USDC
    const secretKey = bs58.decode(process.env.SOLANA_SIGNER_KEY as string);
    const keypair = Keypair.fromSecretKey(secretKey);
    const signer = await createKeypairSignerFromBytes(keypair.secretKey);
    const http = withPaymentInterceptor(axios.create(), signer);

    async function main() {
      const resp = await http.post("https://api.markdown2pdf.ai/markdown", {
        data: { text_body: "# Save this one using X402" },
        options: { document_name: "output.pdf" },
      });
      console.log("Paid tx proof:", resp.headers["x-payment-response"]);
      console.log("Poll:", resp.data.path);
    }

    main().catch(console.error);
    ```

    `x402-axios` intercepts the 402, signs an `X-PAYMENT` payload with your Solana
    keypair, and retries automatically. A future SDK release will expose a first-class
    `onX402PaymentRequired` option on `convertMarkdownToPdf`. See the
    [X402 page](/x402) for protocol details.
  </Tab>
</Tabs>

***
