Make an API request with a custom button (Modern Ribbon)

Learn how to update an account using VAT data through an API call (Power Automate) and JavaScript, using the Modern Ribbon (not Ribbon Workbench)


The challenge

When creating a new account in Dynamics 365, it can be time consuming to enter the correct account data, and there is a risk of mistakes.

In many countries, the company data is publicly available, and the information can usually be accessed through an API call

Let’s make a custom button that starts a Power Automate flow that can get this data, and return it to Dynamics

Solution

In this blog post I will be using the Danish equivalent to VAT (CVR)

I found two good Danish providers - I will continue with the first one (free up to 50 API calls pr. day)

cvrapi.dk
virkdata.dk

Step 1
Create a custom button
We can search the API using either the company name or the VAT number. We will use the VAT number in this exercise.

If you don’t have a field for VAT, create one and insert it into your form.

Go to your app, and add a new button to your main form.

I haved named my button update account data

We will return to the button again.

Step 2
Create a Power Automate Flow
Go to make.powerautomate.com and create a new Automated Cloud Flow (you might also consider setting this flow up as a Logic App in Azure)

Step 3
Your first action (trigger action) is when a HTTP request is received

In the Request Body JSON Schema insert the following code

{
    "type": "object",
    "properties": {
        "id": {
            "type": "string"
        }
    }
}

Change who can trigger the flow to anyone

Warning anybody who has the HTTP POST URL could potentially trigger the flow

Step 4
Insert a get a row by ID action and insert the id from the trigger output. Make sure that you are also limiting the output in select columns to the internal name of your VAT-number field (we want this flow to run as fast as possible)

Step 5
Insert a new action called HTTP

The documentation for CVRAPI is in Danish, but it is simple - the request for a search is https://cvrapi.dk/api?search= & account name or VAT number. The documentation also explains that a country code is always required, so we must also insert &country=dk

Here is two API examples

https://cvrapi.dk/api?search=Microsoft&country=dk
https://cvrapi.dk/api?search=13612870&country=dk

virkdata.dk requires an API Key.

To use an API key, change authentication to raw and insert the API key in value (this is not needed for cvrapi)

Now our HTTP request will look like this

You can check out the data that the API is returning by following this link (this will also give you the internal names that we are going to use in the next step)

cvrapi.dk/api?search=13612870&country=dk

Step 6
Next we want to update our account, so the next action should be update a row

Row ID should be id from our trigger

We will update Account name, ZIP code and Main Phone, you can choose other fields from the API if you like (if you know parse from JSON, you can also do that instead)

Account name outputs('HTTP')['body']?['name']
ZIP code outputs('HTTP')['body']?['zipcode']
Main Phone outputs('HTTP')['body']?['phone']

Step 7
Our last action is going to be response

Make sure the status code is 200

Step 8
Save your flow.

If you go to the top of the flow you will see that a link is now available in the HTTP POST URL, we are going to use this link later

If you want to improve your search result you could change the flow so that if a VAT number is available we will use this as a search query - if not, then we use the company name

The code

Now we are going to use some code. I left some comments in the code, so you can follow along what is happening in the console

Explanation of the code
The code is triggered by the main function, which is then getting the context of the form (FormContext). We are then asking the code to return the ID of the record.

The record is returned with a { and a }, so we are “cleaning” up the ID by removing the curly brackets with .replace

When the user is clicking the new button, we are asking if the user wants to continue with Xrm.Navigation.openConfirmDialog

If user selects no, we are cancelling the script. If the user select yes, we are then posting the EntityID that we got using FormContext.data.entity.getId() to the Power Automate flow with the PostRecordID function

We are then showing a FormNotification to tell the user that a flow is running.

If the HTTP request fails, we are telling the user that we could not find the company (the documentation can send other errors, but we don’t handle them in this exercise)

If the HTTP request succeed, Power Automate will update the record for us, and we will refresh the form with FormContext.data.refresh(false) and then update the FormNotification

Last, we will make sure to clear the form notification after 10 seconds with the setTimeout function

Copy below code and save it as a .js file - remember to insert your own HTTP POST URL in the code

I have named my JavaScript file updateCompanyData.js

// Get values from the form context
const getEntityValues = async (FormContext) => {
	console.log("Called");

	const entityId = FormContext.data.entity.getId().replace("{", "").replace("}", "");
	console.log(entityId);

	return entityId;
};

// Send the entityId to Power Automate
const postEntityId = async (recordId) => {
	const requestOptions = {
		method: 'POST',
		headers: {
			'Content-Type': 'application/json'
		},
		body: JSON.stringify({
			id: recordId
		}),
		redirect: 'follow'
	};

	const url = `INSERT HTTP POST URL HERE`;

	try {
		const response = await fetch(url, requestOptions);
		if (response.ok) {
			console.log("Flow completed successfully.");
			return true;
		} else {
			console.log("Flow completed with error:", response.status, response.statusText);
			return false;
		}
	} catch (error) {
		console.log('Error executing flow:', error);
		return false;
	}
};

// Main function to handle the button click
async function main(FormContext) {
	try {
		console.log("Started");

		const entityId = await getEntityValues(FormContext);

		// Show confirmation dialog
		const confirmStrings = { text: "Do you want to update the account data?", title: "Update account data" };
		const confirmOptions = { height: 200, width: 450 };
		const confirmed = await new Promise((resolve) => {
			Xrm.Navigation.openConfirmDialog(confirmStrings, confirmOptions).then(
				function (success) {
					resolve(success.confirmed);
				}
			);
		});

		if (!confirmed) {
			console.log("User canceled the action.");
			return;
		}

		// Save  the form before running the flow
		await FormContext.data.entity.save();

		FormContext.ui.setFormNotification("Getting company data, please wait...", "INFO", entityId)

		const flowCompleted = await postEntityId(entityId);

		if (flowCompleted) {
			console.log("Flow execution successful, refreshing form...");
			// Refresh the form
			FormContext.data.refresh(false);	
			console.log("Form refreshed.");
			// Success message to user
			FormContext.ui.setFormNotification("Company data has been updated successfully", "INFO", entityId)
		} else {
			console.log("Flow execution failed or returned non-200 status.");
			FormContext.ui.setFormNotification("Error: Could not find any company data. Is the VAT number correct?", "ERROR", entityId)
		}
			//Clear the success message
			setTimeout(function() {
			FormContext.ui.clearFormNotification(entityId);
			}, 10000);

		console.log("Main function completed");
	} catch (error) {
		console.log(`Main function failed: ${error}`);
	} finally {
		console.log("Main function stopped");
	}
}

Step 9
Let’s go back to our button.

In the command bar click Add library and then select New web resource

Upload your JavaScript file and give it a name. I have called mine updateCompanyData

Step 10
After you have uploaded your new web resource, search for the resource, select it and click add

Step 10
In the command bar menu insert main in function name and under Parameter 1 select PrimaryControl

Step 11
Visibility
We don’t want the button to be available if the form is new (the user has not clicked save), or if the VAT field is empty.

In the command bar menu under visibility, change the value to Show on condition from formula and insert the following code

If(FormMode = FormMode.New || IsBlank(Self.Selected.Item.VAT), false, true)

Change .VAT to the display name of your column, if it is not VAT

The only thing that is left to do is to click Save and Publish

Running the code

After the code has been uploaded, we can now make an update of the company data with a click of a button

If the form is new we won’t show the button just yet

We won’t show the button either if the VAT field is empty

Here the user has inserted the VAT number, and the button update account data is visible - dare we press it?

We ask the user if they want to update the company data

We are now telling the user that the data is being collected (usually takes 1-2 seconds)

Here we have updated the data, and is telling the user it has been updated successfully

Here we are telling the user that a company could not be found (since the VAT is wrong)

I am sure you can find equivalent API services for VAT in your country. If not, I recommend you try openweathermap.org so that you can always update the current weather for the company you are going to visit next

And finally a little disclaimer - I am a low-code dude, the code above might not be 100% correct - this blog is just me trying to share what I am learning :)


See also