Skip to main content
C1 provides a dedicated API endpoint to convert any generated artifact into a downloadable PDF file. This guide covers the end-to-end process, which involves creating an API route on your backend and connecting it to the C1 frontend components.

How it Works

The PDF export process involves two main steps and is designed to keep your API keys secure:
  1. Frontend Trigger: The user initiates the export from the UI. The <C1Component> provides a exportParams string to a callback function in your code.
  2. Backend Endpoint: Your frontend sends these exportParams to a dedicated endpoint on your own backend. Your backend then securely calls the C1 PDF Export API with your API key and streams the resulting PDF file back to the user’s browser for download.
You must create a backend endpoint. The C1 PDF Export API cannot be called directly from the frontend as it would expose your secret API key.

Implement the Backend Endpoint

Your backend needs an API route that will receive the request from your frontend, call the C1 API, and forward the PDF response. PDF Export API Endpoint: POST /v1/artifact/pdf/export
app/api/export-pdf/route.ts (Next.js App Router)
import { NextRequest, NextResponse } from 'next/server';

export async function POST(req: NextRequest) {
  const { exportParams } = await req.json();

  try {
    const pdfResponse = await fetch('https://api.thesys.dev/v1/artifact/pdf/export', {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${process.env.THESYS_API_KEY}`,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({ exportParams }),
    });

    if (!pdfResponse.ok) {
      throw new Error(`Failed to export PDF: ${pdfResponse.statusText}`);
    }

    // Stream the PDF back to the client
    return new NextResponse(pdfResponse.body, {
      headers: {
        'Content-Type': 'application/pdf',
        'Content-Disposition': 'attachment; filename="artifact.pdf"',
      },
    });

  } catch (error) {
    const message = error instanceof Error ? error.message : 'An unknown error occurred';
    return NextResponse.json({ error: message }, { status: 500 });
  }
}

Implement the Frontend Handler

The <C1Component> provides an exportAsPdf prop, which accepts a function. This function is called when the user clicks the export button in the artifact’s UI. It receives the exportParams and a suggested title for the file. Your implementation should call your backend endpoint and use the response to trigger a file download in the browser.
import { C1Component, ThemeProvider } from "@thesysai/genui-sdk";

function ArtifactWithExport({ c1Response }: { c1Response: string }) {
  return (
    <ThemeProvider>
      <C1Component
        c1Response={c1Response}
        exportAsPdf={handleExport}
      />
    </ThemeProvider>
  );
}

const handleExport = async ({ exportParams, title }: { exportParams: string, title: string }) => {
    try {
      // 1. Call your backend endpoint with the exportParams
      const response = await fetch("/api/export-pdf", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ exportParams }),
      });

      if (!response.ok) {
        throw new Error("Failed to download PDF.");
      }

      // 2. Get the PDF data as a blob
      const blob = await response.blob();

      // 3. Create a temporary URL and trigger the download
      const url = window.URL.createObjectURL(blob);
      const a = document.createElement("a");
      a.href = url;
      const filename = (title || 'artifact').replace(/\.pdf$/i, '');
      a.download = `${filename}.pdf`;
      document.body.appendChild(a);
      a.click();
      
      // 4. Clean up the temporary URL
      window.URL.revokeObjectURL(url);
      a.remove();

    } catch (error) {
      console.error("Export failed:", error);
      // Handle error, e.g., show a notification to the user
    }
  };

Usage with <C1Chat>

You can also enable PDF export in a conversational context. The exportAsPdf function can be passed via the customizeC1 prop to either <C1Chat> or the useThreadManager hook.
<C1Chat
  apiUrl="/api/chat"
  customizeC1={{
    exportAsPdf: handleExport, // Use the same handler function
  }}
/>