How to attach a file to HubSpot contact through APIs, in Power Automate

With this article, the issues and solutions I am trying to address are:

  1. How to use HubSpot APIs
  2. How to upload a file to HubSpot
  3. How to make association between a Contact and the uploaded file
  4. How to achieve all of these using Power Automate

On a high level, below is the list of API tasks we have to do to achieve this. We have to make requests to three calls (one less if you have Customer ID already).

High level Power Automate flow

See below the high level flow I have created to demonstrate:

Prerequisites

  1. Basic knowledge in HubSpot. We would be interacting the CRM Contacts and Library
  2. Good knowledge in Power Automate
  3. A HubSpot account – Sandbox / Developer
  4. Create a Private App in HubSpot
  5. Get an Access Token
  6. Create a sample contact in HubSpot (for testing)
  7. Create a folder in the HubSpot Library (optional)

Detailed Flow

In this example, I am using a HTTP Request Trigger, which starts when we POST a file and a email-id (I used it to represent a customer in HubSpot contact form). I am doing some

Set the stage

For those struggling to get few items from the POST’ed content, find below details for your reference.

Using Postman to trigger a Power Automate HTTP flow

A. Get File content

triggerOutputs()['body']['$multipart'][0]['body']

B. Get filename

Honestly, I am not sure if there is a straight forward method available. This is my way of parsing filename from the Content-Disposition string:

// Input sample: "headers": {
//        "Content-Disposition": "form-data; name=\"email\"",
//        "Content-Length": "25"
//      }

// Power fx
trim(concat(split(split(triggerOutputs()['body']['$multipart'][0]['headers']['Content-Disposition'], 'filename="')[1], '"')[0]))

C. Get Email

triggerOutputs()['body']['$multipart'][1]['body']['$content']

HubSpot API Calls

Since the APIs for fetching Contact ID and Uploaded File ID are independent tasks, I am executing them in parallel.

A. Upload file, and get file id

I have used only minimum number parameters to avoid the confusion. I am uploading the file to a folder in the library.

Find the POST’ing JSON script for the Body. I see many people are struggling to make this correct.

{
  "$content-type": "multipart/form-data",
  "$multipart": [
    {
      "headers": {
        "Content-Disposition": "form-data; name=\"folderPath\""
      },
      "body": "/Sample Folder"
    },
    {
      "headers": {
        "Content-Disposition": "form-data; name=\"file\"; filename=\"@{variables('Filename')}\""
      },
      "body": @{variables('file')}
    },
    {
      "headers": {
        "Content-Disposition": "form-data; name=\"options\""
      },
      "body": {
        "access": "PRIVATE"
      }
    }
  ]
}

Upon successful upload, you will be receiving a JSON response with File ID.

B. Get the Customer ID of a contact using email as parameter.

This is a straight forward task. Just call the API using GET. Example: https://api.hubapi.com/crm/v3/objects/contacts/john@mathew.com?idProperty=email

C. Create Contact-File association

Once you have FileID and Contact ID, now you can POST a create-note API call to https://api.hubapi.com/crm/v3/objects/notes

Below is the JSON body you have to use:

{
    "properties": {
        "hs_timestamp": "2024-07-30T10:30:00.000Z",
        "hs_attachment_ids": "<File ID>"
    },
    "associations": [
        {
            "to": {
                "id": "<Customer ID>"
            },
            "types": [
                {
                    "associationCategory": "HUBSPOT_DEFINED",
                    "associationTypeId": 10
                }
            ]
        }
    ]
}

Note: associationTypeId is the magic number which tells the API to make Contact-File association. Please check the documentation for more association types.

Find the Power Automate action view:

Verify if the flow has worked

Go to HubSpot, select contact and you should be able to see the file attached.

Additionally, if you go to the Library -> Files -> Sample Folder, you can see the same file appearing there.

How to read Azure KeyVault secrets using Managed Identity in .NET Framework 4.8 C#

Using Managed Identity to deploy azure resources is considered best practice as it reduces the overhead of keeping additional credentials (tokens/passwords) in config files. This article is about accessing Auzre KeyVault using Managed Identity. I am using .NET Framework 4.8 version for this tutorial.

Step 1 – Create KeyVault and secrets

First, just go to Azure Portal and create necessary secret values for testing. I would go with a “testkey” and a dummy value.

(I am assuming you know the basics of Azure Portal and knows how to create an azure resource such as KeyVault)

image

Also, please take a note of the “Vault URI” you can see in the “Overview” section. We would require it in the C# Code.

Step 2 – Create Sample .NET App

Next, open Visual Studio (I have used 2022) and start a new project. I have used a .NET Framework 4.8 Console Application.

Step 3 – Install necessary NuGet packages

We require two major packages for this project. Install these:

1. Azure.Identity
2. Azure.Security.KeyVault.Secrets

image

Step 4 – Coding!

This is the sample code I have used. Make sure to replace with your keyvault URL.

using System;
using Azure.Identity;
using Azure.Security.KeyVault.Secrets;

namespace kvtest
{
     internal class Program
     {
         static void Main(string[] args)
         {
             SecretClient secretClient = new SecretClient(new Uri("https://your-keyvault.vault.azure.net/"), new DefaultAzureCredential());
             var secret = secretClient.GetSecret("testkey");
             Console.WriteLine(secret.Value.Value);

            Console.ReadKey();

        }
     }
}

Notice the “DefaultAzureCredential()”, which does the trick of our Managed Identity, without providing plain credentials here.

Step 4 – Login to Azure

If you “run” your app at this stage, you will end up getting an error like the one below. This is because, currently you do not have any connection between your laptop and azure portal. This application will work if you host this in Azure, in any resources like App Service but you cannot run this in your developer laptop/machine if you want to debug.

image

To make your app debug-able in your machine, you have to let Visual Studio login to Azure.

Go to Tools –> Options –> Azure Service Authentication

and, login to your account there.

image

Step 5 – Execute!

Now we are all set for building and running the app. Just hit F5!

image

Power Automate: POST a file to trigger a HTTP flow and upload to SharePoint

My use case here was to store the resumes uploaded by candidate in a website directly in a SharePoint list.

Below is the high level flow the process, created in Power Automate (same you can do in Azure Logic Apps as well with similar steps)

image

Here is the output, i.e., data and file uploaded to SharePoint/Office 365

image

The HTTP call is expected to be made from the external application such as a web page but for the purpose of testing, here is the Postman screen used:

image

Detailed flow

Let us start with an HTTP Request Trigger. You will get a URL which looks like in the below screenshot. This is the URL you will be using. By default this will be using anonymous auth, but you can secure it using few methods, like this one.

image

Parsing Json

This is an optional step. I wanted to pass some parameters also in addition to the file upload, but I chose not to send individually but as a json string. Since it is a Json string, I have to parse it to extract values from it.

image

This is the sample input I have. Refer to postman screenshot above.

{

"position": "Java Developer",

"firstname": "Praveen",

"lastname": "Nair",

"email": "praveennnn@abcdef.com",

"phone": "+9715555555",

"urls": ["http://www.google.com", "https://www.adfolks.com"]

}

Create SharePoint List row

Creating a list row and attaching the file is a two step process. First we have to create the row, then using that ID, we have to attach the uploaded file.

image

Final notes

The last send-mail component you see is just to notify someone in recruitment team that there is a job application logged in SharePoint. Same you can achieve using SharePoint notification features so you may ignore it.

I found that getting filename from the uploaded payload is not so straightforward in Power Automate (someone correct me if I am wrong), but anyways I did’nt need that because the logic I had to use here is to create a custom filename using the candidate’s name and adding the extension we get from the content-type.