Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,228 @@
---
title: Generate a PDF from Twenty
description: Create a workflow to generate and attach a PDF (such as a quote) to a record.
---

Automatically generate or fetch a PDF and attach it to a record in Twenty. This is commonly used to create quotes, invoices, or reports that are linked to Companies, Opportunities, or other objects.

## نظرة عامة

This workflow uses a **Manual Trigger** so users can generate a PDF on demand for any selected record. A **Serverless Function** handles:

1. Downloading the PDF from a URL (from a PDF generation service)
2. Uploading the file to Twenty
3. Creating an Attachment linked to the record

## المتطلبات الأساسية

Before setting up the workflow:

1. **Create an API Key**: Go to **Settings → APIs** and create a new API key. You'll need this token for the serverless function.
2. **Set up a PDF generation service** (optional): If you want to dynamically generate PDFs (e.g., quotes), use a service like Carbone, PDFMonkey, or DocuSeal to create the PDF and get a download URL.

## إعداد خطوة بخطوة

### الخطوة 1: تهيئة المشغّل

1. Go to **Workflows** and create a new workflow
2. Select **Manual Trigger**
3. Choose the object you want to attach PDFs to (e.g., **Company** or **Opportunity**)

<Tip>
With a Manual Trigger, users can run this workflow using a button that appears on the top right once a record is selected, to generate and attach a PDF.
</Tip>

### Step 2: Add a Serverless Function

1. Add a **Serverless Function** action
2. Create a new function with the code below
3. Configure the input parameters

#### Input Parameters

| Parameter | القيمة |
| ----------- | ----------------------- |
| `companyId` | `{{trigger.object.id}}` |

<Note>
If attaching to a different object (Person, Opportunity, etc.), rename the parameter accordingly (e.g., `personId`, `opportunityId`) and update the serverless function.
</Note>

#### Serverless Function Code

```typescript
export const main = async (
params: { companyId: string },
) => {
const { companyId } = params;

// Replace with your Twenty GraphQL endpoint
// Cloud: https://api.twenty.com/graphql
// Self-hosted: https://your-domain.com/graphql
const graphqlEndpoint = 'https://api.twenty.com/graphql';

// Replace with your API key from Settings → APIs
const authToken = 'YOUR_API_KEY';

// Replace with your PDF URL
// This could be from a PDF generation service or a static URL
const pdfUrl = 'https://your-pdf-service.com/generated-quote.pdf';
const filename = 'quote.pdf';

// Step 1: Download the PDF file
const pdfResponse = await fetch(pdfUrl);

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

const pdfBlob = await pdfResponse.blob();
const pdfFile = new File([pdfBlob], filename, { type: 'application/pdf' });

// Step 2: Upload the file via GraphQL multipart upload
const uploadMutation = `
mutation UploadFile($file: Upload!, $fileFolder: FileFolder) {
uploadFile(file: $file, fileFolder: $fileFolder) {
path
}
}
`;

const uploadForm = new FormData();
uploadForm.append('operations', JSON.stringify({
query: uploadMutation,
variables: { file: null, fileFolder: 'Attachment' },
}));
uploadForm.append('map', JSON.stringify({ '0': ['variables.file'] }));
uploadForm.append('0', pdfFile);

const uploadResponse = await fetch(graphqlEndpoint, {
method: 'POST',
headers: { Authorization: `Bearer ${authToken}` },
body: uploadForm,
});

const uploadResult = await uploadResponse.json();

if (uploadResult.errors?.length) {
throw new Error(`Upload failed: ${uploadResult.errors[0].message}`);
}

const filePath = uploadResult.data?.uploadFile?.path;

if (!filePath) {
throw new Error('No file path returned from upload');
}

// Step 3: Create the attachment linked to the company
const attachmentMutation = `
mutation CreateAttachment($data: AttachmentCreateInput!) {
createAttachment(data: $data) {
id
name
}
}
`;

const attachmentResponse = await fetch(graphqlEndpoint, {
method: 'POST',
headers: {
Authorization: `Bearer ${authToken}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
query: attachmentMutation,
variables: {
data: {
name: filename,
fullPath: filePath,
companyId,
},
},
}),
});

const attachmentResult = await attachmentResponse.json();

if (attachmentResult.errors?.length) {
throw new Error(`Attachment creation failed: ${attachmentResult.errors[0].message}`);
}

return attachmentResult.data?.createAttachment;
};
```

### Step 3: Customize for Your Use Case

#### To attach to a different object

Replace `companyId` with the appropriate field:

| كائن | اسم الحقل |
| ---------- | -------------------- |
| الشركة | `companyId` |
| شخص | `personId` |
| الفرصة | `opportunityId` |
| كائن مخصّص | `yourCustomObjectId` |

Update both the function parameter and the `variables.data` object in the attachment mutation.

#### To use a dynamic PDF URL

If using a PDF generation service, you can:

1. First make an HTTP Request action to generate the PDF
2. Pass the returned PDF URL to the serverless function as a parameter

```typescript
export const main = async (
params: { companyId: string; pdfUrl: string; filename: string },
) => {
const { companyId, pdfUrl, filename } = params;
// ... rest of the function
};
```

### الخطوة 4: الاختبار والتفعيل

1. Save the workflow
2. Navigate to a Company record
3. Click the **⋮** menu and select your workflow
4. Check the **Attachments** section on the record to verify the PDF was attached
5. فعّل سير العمل

## Combining with PDF Generation Services

For creating dynamic quotes or invoices:

### Example: Generate Quote → Attach PDF

| الخطوة | الإجراء | الغرض |
| ------ | ------------------------ | ---------------------------------------- |
| 1 | Manual Trigger (Company) | User initiates on a record |
| 2 | البحث عن سجل | Get Opportunity or line item details |
| 3 | طلب HTTP | Call PDF generation API with record data |
| 4 | Serverless Function | Download and attach the generated PDF |

### Popular PDF Generation Services

* **Carbone** - Template-based document generation
* **PDFMonkey** - Dynamic PDF creation from templates
* **DocuSeal** - Document automation platform
* **Documint** - API-first document generation

Each service provides an API that returns a PDF URL, which you can then pass to the serverless function.

## استكشاف الأخطاء وإصلاحها

| المشكلة | الحل |
| ---------------------------- | ---------------------------------------------------------- |
| "Failed to download PDF" | Check the PDF URL is accessible and returns a valid PDF |
| "Upload failed" | Verify your API key is valid and has write permissions |
| "Attachment creation failed" | Ensure the object ID field name matches your target object |

## ذات صلة

* [مشغلات سير العمل](/l/ar/user-guide/workflows/capabilities/workflow-triggers)
* [Serverless Functions](/l/ar/user-guide/workflows/capabilities/workflow-actions#serverless-function)
* [Generate a Quote or Invoice from Twenty](/l/ar/user-guide/workflows/how-tos/connect-to-other-tools/generate-quote-or-invoice-from-twenty)
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ Body: {{code.invoice}}

## ذات صلة

* [Generate a PDF from Twenty](/l/ar/user-guide/workflows/how-tos/connect-to-other-tools/generate-pdf-from-twenty) — attach generated PDFs to records
* [مشغلات سير العمل](/l/ar/user-guide/workflows/capabilities/workflow-triggers)
* [إجراءات سير العمل](/l/ar/user-guide/workflows/capabilities/workflow-actions)
* [أتمتة Closed Won](/l/ar/user-guide/workflows/how-tos/crm-automations/closed-won-automations)
Loading