[✅ Solution] Autentique API FormData requests

I’ve been working with Autentique GraphQL API and it is requiring the use of FormData JS objects to deal with “multipart/form-data” requests.

Document create documentation: Autentique (scroll down to Node.js approach)

My code in Workflow:

const record = await tape.Record.get(12393083)
const file = record.data.fields.find(field => field.external_id === 'files');
const fileLink = file.values[0].value.link;

let response = await http.get(fileLink);

// Dados do arquivo em forma de buffer
const fileBuffer = Buffer.from(response.data);

const autentique_base_url = "https://api.autentique.com.br/v2/graphql";
const autentique_access_token = "token_here";
const autentique_filename = "Contrato de Teste.pdf";
const autentique_email = 'graco.silva@gentilnegocios.com.br';

const requestHeaders = {
  'Authorization': `Bearer ${autentique_access_token}`, // Token de autorização
  'Content-Type': "multipart/form-data", // Tipo de conteúdo da solicitação
};

const query = "mutation CreateDocumentMutation($document: DocumentInput!, $signers: [SignerInput!]!, $file: Upload!) {createDocument(document: $document, signers: $signers, file: $file) {id name refusable sortable created_at signatures { public_id name email created_at action { name } link { short_link } user { id name email }}}}";

const variables = {
      "document":{
         "name": autentique_filename
      },
      "signers":[
         {
            "email": autentique_email,
            "action":"SIGN"
         }
      ],
      "file": fileBuffer
};

response = await http.upload(
    {
        url: autentique_base_url,
        headers: requestHeaders,
        data: {
            query: query,
            variables: variables,
        },
    },
    {
        url: fileLink
    }
);
console.log(JSON.stringify(response));

That causes a HTTP 500 error.

Have you ever tried similar works to upload a document to sign? Can you help me with that, maybe with Tape new features?

1 Like

Here is my approach with the custom payload as parts.

// Getting a file from a record
const record = await tape.Record.get(12393083);

const file = record.data.fields.find(field => field.external_id === 'files');

const fileValue = file.values[0] ? file.values[0].value : {};

const fileLink = fileValue ? fileValue.link : '';

const fileName = fileValue ? fileValue.name : '';

const fileContentType = fileValue ? fileValue.mimetype : 'application/octet-stream';

// Download the file
let response = await http.get(fileLink);

const fileContent = response.data;

const autentiqueBaseUrl = "https://api.autentique.com.br/v2/graphql";

const autentiqueAccessToken = "token_here";

const autentiqueEmail = 'graco.silva@gentilnegocios.com.br';

// Create a unique boundary for the multipart/form-data

const boundary = '----Autentique123';

// Construct the multipart/form-data payload

const parts = [

`--${boundary}\r\nContent-Disposition: form-data; name="operations"\r\n\r\n`,

JSON.stringify({

query: "mutation CreateDocumentMutation($document: DocumentInput!, $signers: [SignerInput!]!, $file: Upload!) {createDocument(document: $document, signers: $signers, file: $file) {id name refusable sortable created_at signatures { public_id name email created_at action { name } link { short_link } user { id name email }}}}",

variables: {

document: { name: fileName },

signers: [{ email: autentiqueEmail, action: "SIGN" }],

file: null

}

}),

`\r\n--${boundary}\r\nContent-Disposition: form-data; name="map"\r\n\r\n`,

JSON.stringify({ "file": ["variables.file"] }),

`\r\n--${boundary}\r\nContent-Disposition: form-data; name="file"; filename="${fileName}"\r\nContent-Type: ${fileContentType}\r\n\r\n`,

fileContent,

`\r\n--${boundary}--`

];

const postData = parts.join('');

// Set the headers

const headers = {

'Authorization': `Bearer ${autentiqueAccessToken}`,

'Content-Type': `multipart/form-data; boundary=${boundary}`,

// 'Content-Length': String(Buffer.byteLength(body))

};

// Send the request

http.post(autentiqueBaseUrl, {

headers: headers,

data: postData

})

.then(response => {

console.log(JSON.stringify(response));

})

.catch(error => {

console.error(error);

});
2 Likes

Sorry for the inconvenient but my previous solution had a lack.

The document was created as blank due to the payload as a string considered like a txt file.

I have to convert it to a Buffer but with http module the API is responding wrong. It generates a HTTP 500 error.

const fileLink = remotehttpcall_field_files_file_url;
const fileName = remotehttpcall_field_files_filename;

if(!fileLink) {
    console.warn('File link not found!');    
}else{
    // Create a unique boundary for the multipart/form-data
    const boundary = '----Autentique123';
    
    // Download the file
    let {data: fileContent} = await http.get(fileLink, {responseEncoding: 'binary'});

    // Autentique credentials
    const autentiqueBaseUrl = "https://api.autentique.com.br/v2/graphql";
    const autentiqueAccessToken = "token_here";
    const autentiqueEmail = 'graco.silva@gentilnegocios.com.br';

    const operations = JSON.stringify({
        query: "mutation CreateDocumentMutation($document: DocumentInput!, $signers: [SignerInput!]!, $file: Upload!) {createDocument(document: $document, signers: $signers, file: $file) {id name refusable sortable created_at signatures { public_id name email created_at action { name } link { short_link } user { id name email }}}}",
        variables: {
            document: { name: fileName },
            signers: [{ email: autentiqueEmail, action: "SIGN" }],
            file: null
        }
    });
    const mapContent = JSON.stringify({ "file": ["variables.file"] });

    // Data string
    let data = "";
    data += `--${boundary}\r\n`;
    data += 'Content-Disposition: form-data; name="operations"\r\n\r\n';
    data += `${operations}\r\n`;

    data += `--${boundary}\r\n`;
    data += 'Content-Disposition: form-data; name="map"\r\n\r\n';
    data += `${mapContent}\r\n`;

    data += `--${boundary}\r\n`;
    data += `Content-Disposition: form-data; name="file"; filename="${fileName}"\r\n`;
    data += 'Content-Type: application/pdf\r\n\r\n';

    const payload = Buffer.concat([
        Buffer.from(data, 'utf8'),
        Buffer.from(fileContent, 'binary'),
        Buffer.from(`\r\n--${boundary}--\r\n`, 'utf8'),
    ]);

    // Set the headers
    const headers = {
        'Authorization': `Bearer ${autentiqueAccessToken}`,
        'Content-Type': `multipart/form-data; boundary=${boundary}`
    };

    // Send the request
    http.post(autentiqueBaseUrl, {
        headers: headers,
        data: payload
    })
    .then(response => {
        console.log(JSON.stringify(response));
    })
    .catch(error => {
        console.error(error);
    });
}

When I send the data buffer inside a “Send HTTP request” action it is processed well, but the document remains blank.

Can you help me with that, @Tim @Leo?

Best,
Graco.

1 Like

Hi @gracosilva,

thank you very much for your detailed description of your problem.
We have scheduled a slot today to analyze your issue in detail and to find a solution for you.
I will get back to you as soon as we have the first results.

Cheers
Leo

Hi @gracosilva

I checked your code yesterday in detail and could not find any errors, I would have built it very similar to you. But there is most certainly a way to connect Autentique without throwing a 500 error.

But for that we have to extend our lab and make an account with Autentique ourselves to test different approaches.

Unfortunately, we don’t have the time during the week, but we’ll make time for this at the weekend and should have a solution for you by Monday at the latest.

Best,
Ben

Hi Ben. Don’t worry.

I’m only struggling with the blank content when running with a Send HTTP request action. That is, another separate block. It didn’t generate HTTP 500 error no more.

Best,
Graco.

Hi @gracosilva,

great that you have already solved the HTTP 500 error.
After my tests yesterday I am very sure that the blank content is also caused by an interaction with Autentique.

That’s why we need the lab and a dedicated account with the service to understand the problem. We will take the time this weekend.

Best
Ben

Hi Ben! Great for this moment.

I’m really enjoying your effort to get the job done and help my team at Gentil Negócios.

I could share the Autentique credentials with you in DM, for instance on Microsoft Teams. What do you think?

Best,
Graco.

@gracosilva thanks for your patience - after we reproduced your use case inside our lab using an Autentique account, we figured it is hardly possible with the regular http.post method. In order to allow using the form data spec inside of workflow automations, we added a new method postFormData to the Workflow execution context HTTP client that handles the implementation details. With that, it is now easily possible to create the PDF documents from Tape.

We added a simple guide that concludes your use case:

Be sure to let us know how this works for you, and hit me up if you have questions.

Cheers & happy signing in :brazil:
Tim

3 Likes