From b00293055476ee692b668c07170c42be58e0e8f6 Mon Sep 17 00:00:00 2001 From: BOHEUS <56270748+BOHEUS@users.noreply.github.com> Date: Wed, 1 Apr 2026 17:15:43 +0300 Subject: [PATCH] Update documentation on how to upload a file (#19197) As per title, update a documentation on how to upload a file given increasing amount of questions for this problem CC: @StephanieJoly4 --------- Co-authored-by: Etienne <45695613+etiennejouan@users.noreply.github.com> --- .../generate-pdf-from-twenty.mdx | 79 +++++++++++++------ 1 file changed, 57 insertions(+), 22 deletions(-) diff --git a/packages/twenty-docs/user-guide/workflows/how-tos/connect-to-other-tools/generate-pdf-from-twenty.mdx b/packages/twenty-docs/user-guide/workflows/how-tos/connect-to-other-tools/generate-pdf-from-twenty.mdx index faf463062d..a572279155 100644 --- a/packages/twenty-docs/user-guide/workflows/how-tos/connect-to-other-tools/generate-pdf-from-twenty.mdx +++ b/packages/twenty-docs/user-guide/workflows/how-tos/connect-to-other-tools/generate-pdf-from-twenty.mdx @@ -54,11 +54,12 @@ export const main = async ( params: { companyId: string }, ) => { const { companyId } = params; - - // Replace with your Twenty GraphQL endpoint + // Replace with your Twenty GraphQL endpoints (/metadata for metadata and files or /graphql for your records) // Cloud: https://api.twenty.com/graphql // Self-hosted: https://your-domain.com/graphql - const graphqlEndpoint = 'https://api.twenty.com/graphql'; + + const metadataGraphqlEndpoint = 'https://api.twenty.com/metadata'; + const dataGraphqlEndpoint = 'https://api.twenty.com/graphql'; // Replace with your API key from Settings → APIs const authToken = 'YOUR_API_KEY'; @@ -78,11 +79,40 @@ export const main = async ( const pdfBlob = await pdfResponse.blob(); const pdfFile = new File([pdfBlob], filename, { type: 'application/pdf' }); - // Step 2: Upload the file via GraphQL multipart upload + const fieldMetadataIdQuery = ` + query FindUploadFileFieldMetadataId { + objects { + edges { + node { + nameSingular + fieldsList { + id + name + } + } + } + } + } + `; + + // Step 2: Find a fieldMetadataId of "Attachment file" field in Attachments object with GraphQL API + const response = await fetch(metadataGraphqlEndpoint, { + method: 'POST', + headers: { + Authorization: `Bearer ${authToken}` + }, + body: { + query: fieldMetadataIdQuery, + } + }); + const result = await response.json(); + const uploadFileFieldMetadataId = result.data.objects.edges.find(object => object.node.nameSingular === 'attachment').node.fieldsList.find(field => field.name === 'file').id; + + // Step 3: Upload the file via GraphQL multipart upload const uploadMutation = ` - mutation UploadFile($file: Upload!, $fileFolder: FileFolder) { - uploadFile(file: $file, fileFolder: $fileFolder) { - path + mutation UploadFilesFieldFile($file: Upload!, $fieldMetadataId: String!) { + uploadFilesFieldFile(file: $file, fieldMetadataId: $fieldMetadataId) { + id } } `; @@ -90,12 +120,12 @@ export const main = async ( const uploadForm = new FormData(); uploadForm.append('operations', JSON.stringify({ query: uploadMutation, - variables: { file: null, fileFolder: 'Attachment' }, + variables: { file: null, fieldMetadataId: uploadFileFieldMetadataId }, })); uploadForm.append('map', JSON.stringify({ '0': ['variables.file'] })); uploadForm.append('0', pdfFile); - const uploadResponse = await fetch(graphqlEndpoint, { + const uploadResponse = await fetch(metadataGraphqlEndpoint, { method: 'POST', headers: { Authorization: `Bearer ${authToken}` }, body: uploadForm, @@ -107,15 +137,15 @@ export const main = async ( throw new Error(`Upload failed: ${uploadResult.errors[0].message}`); } - const filePath = uploadResult.data?.uploadFile?.path; + const fileId = uploadResult.data?.uploadFilesFieldFile?.id; - if (!filePath) { - throw new Error('No file path returned from upload'); + if (!fileId) { + throw new Error('No file id returned from upload'); } - // Step 3: Create the attachment linked to the company + // Step 4: Create the attachment linked to the company const attachmentMutation = ` - mutation CreateAttachment($data: AttachmentCreateInput!) { + mutation CreateOneAttachment($data: AttachmentCreateInput!) { createAttachment(data: $data) { id name @@ -123,7 +153,7 @@ export const main = async ( } `; - const attachmentResponse = await fetch(graphqlEndpoint, { + const attachmentResponse = await fetch(dataGraphqlEndpoint, { method: 'POST', headers: { Authorization: `Bearer ${authToken}`, @@ -134,8 +164,13 @@ export const main = async ( variables: { data: { name: filename, - fullPath: filePath, - companyId, + targetCompanyId: companyId, + file: [ + { + fileId: fileId, + label: filename + } + ] }, }, }), @@ -155,14 +190,14 @@ export const main = async ( #### To attach to a different object -Replace `companyId` with the appropriate field: +Replace `targetCompanyId` with the appropriate field: | Object | Field Name | |--------|------------| -| Company | `companyId` | -| Person | `personId` | -| Opportunity | `opportunityId` | -| Custom Object | `yourCustomObjectId` | +| Company | `targetCompanyId` | +| Person | `targetPersonId` | +| Opportunity | `targetOpportunityId` | +| Custom Object | `targetYourCustomObjectId` | Update both the function parameter and the `variables.data` object in the attachment mutation.