Streamscript - Integration from within Salesforce Flows
David Smith
Architecture Lead at GravityLab, CTO for Five To Flow, 16x Salesforce Certified
Setting the scene
I have a Vend shop. Someone purchases one of my products, and that order data, along with product/customer info, gets sent directly into my Salesforce instance, straight into a flow. As an admin, I can adjust the flow easily, and get that data into the objects that I want. I'm not paying for middleware, my data doesn't leave Vend or Salesforce, and it's lightning fast.
It's a pretty interesting scenario, and previously this would involve Apex, developers, and a long development time. Putting this power into the hands of admins has been a goal of Salesforce, and Streamscript does a lot to make this a reality.
What is Streamscript
Streamscript is a free Salesforce package that really opens up options around flow integrations. It offers easy to set up webhooks, callouts to web services, formula-like statements and actions.
It differs from Salesforce's recent offering of HTTP callouts in the following ways:
What is this demo going to cover
In this demo I'm going to send a Vend order to Salesforce and insert it as a Vend_Order__c record. After that, we'll insert each Vend_Order_Line__c, and query Vend for missing product information.
Setting up webhook
Before we can set up the webhook in Vend, we need to get a URL, so that means we need to start in Salesforce.
Install the Streams package in?Sandbox?or?Production.
Go to the Streams app.
Follow the instructions to set up a webhook. You'll need an active site or Community to be able to do this, but you'll be prompted to set this up. One point to note at this stage is that to get logging to work, I had to grant the site guest user access to create 'Event (Streams)' custom object records.
You add the webhook by creating a flow, adding a Streamscript node, and saving it with the right name. There's a set of instructions on the page to guide you through this. Once that's done, the flow will show up under the list of webhooks.
Set up Vend webhook
Take the webhook url from the previous step, and use that to set up your vend webhook.
Security
We want to lock this process down a bit, so that we don't open a webhook to the world. Since we don't really have control over what Vend sends to us (ie can't add a signature), the safest approach is to control it via IP address.
The way to add a streamscript in a flow is to add it an an element:
From here, the following code will prevent requests from other IP addresses from carrying on in the flow.
This pattern is fairly generic, most tools will publish their IP addresses, so you'll be able to add this at the start of your webhook interfaces.
Creating Vend Order
Ok, now we're into the fun stuff.
I want to be able to create a vend order from the payload I receive from Vend. I know what that payload will be from Vend's API documentation. If I needed to review it, I'd probably use something like webhook.site to capture the output. In reality, when I'm building out a process like this, I'd use a middleware like make.com to pass the same payload so that I didn't need to resubmit/update orders directly in Vend.
The essence of Streamscript is that you can create the object inside the Streamscript node and output it as a record in the flow. That means I can replace an Assignment node where I would be setting various field values with a few lines of script.
Here's how I'd manage the vend order insert. We'll dive into this line by line.
# Streamscript - All streamscript scripts start with this line. # is how you comment lines.
$request = Json-Decode $Webhook.request - We're passing the payload from the webhook into the $request object so we can use it later on.
$Vend_Order__c = New-Vend_Order__c - We're setting up our new object. This variable will hold all our values for the insert at the end.
Log `retailer: $request.retailer_id` - This allows us to log in the flow debug log, so we can see variable while debugging.
$Vend_Order__c.Name =$request.payload.data.invoice_number - We're setting the Name field to be the value from the payload. In this case, we're going down a few layers in the JSON using dot notation. Here's what the JSON would look like in this scenario. You can see we go into the payload collection, then the data, then get the invoice_number value.
return $Vend_Order__c
I'm passing the vend order record out of the script. This makes it available to the rest of the flow, as you can see below. I'm just inserting a record now, using the normal insert flow step.
One of the cool parts of streamscript is that you can return multiple variable out of the same node. If I wanted to have the invoice number available as a text variable, I could do something like this:
领英推荐
return -record $Vend_Order__c -text $request.payload.data.invoice_number
Note: You can only return one variable of each type per node. You can't return 2 text variables, but you can return a text variable, a number, a record etc.
Creating vend order lines
Ok, so in lots of JSON payloads, we'll get multiple values in one go, in an array. With Streamscript, you can iterate across this like you would with apex, and return the collection straight out of the script.
Here's an example of some order lines:
We've got an ID for each line, a product ID, and a price. Let's say that's all we want, but we want to get all of them in one go. Again, let's go line by line:
# Streamscript
$request = Json-Decode $Webhook.request - As above.
$Vend_Order_Line__c =[] - Setting up the collection for us to add values into.
foreach ($line in $request.payload.data.line_items) { - This bit's the magic. Now we've got our for loop, and we can just iterate through them.
$l = New-Vend_Order_line__c - Setting up our variable inside the loop.
$l.VendOrderLineItem__c=$line.id
??$l.Vend_Order__c={!varVendOrder}
??$l.Price_per_item__c=$line.price
??$l.VendProductId__c=$line.product_id
Adding in the various lines. You can see that I'm using the value of the vend order from before to set my lookup.
$Vend_Order_Line__c.add($l) - Adding that temp variable to the output collection.
return $Vend_Order_Line__c - Returns that initial variable, as a collection. Then we can just insert the whole lot.
Calling out to Vend
So, from here I want to get the vend product if I don't have a record of it. This needs to be in a separate flow, so that I'm not stuck with an uncommitted DML error, but luckily inserting the vend order lines has generated a trigger action, so I can just use a record triggered flow with an async path to get around that issue.
This isn't a flow tutorial so I'm going to gloss over most of the setup for this process, but here's the script to do the callout.
$token={!varToken}
$url={!formCurrentLine}
I'm setting up an api token from my metadata, so I don't have it hard-coded in the script. I'm also setting up a URL, in case I need to expand this and reference a different vend instance.
$http = GET `$url` {Authorization:`Bearer $token`} - This bit was tricky. It's just setting up the get request with a header, but using substitution with the back ticks. It didn't work unless they were put in this specific order.
$result = JSON-Decode $http.body - We're getting the response from the request, and just treating it like we did with the JSON payload in the original example.
$Vend_Product__c = {
??attributes: {type: 'Vend_Product__c'}
}
$Vend_Product__c.Name =$result.data.variant_name
$Vend_Product__c.Type__c =$result.data.type.name
$Vend_Product__c.VendProductId__c =$result.data.id
return $Vend_Product__c
The rest of this should look familiar now, we're just setting up a vend product, and returning it at the end.
The documentation around the callout on the streamscript site is quite well-written, so if you need to dive into more detail I recommend looking there.
Final Notes
That's it! I hope you found this helpful, please add any questions you might have below.
Salesforce Pro and Data-analyst in Nonprofit sector
1 年Hi David. Just a not to let you know this article has really helped me to be able to catch webhooks. Thanks a lot! Oo - I should have read the note about the backticks earlier. It took me some hours to figure out why building the url was not working.
Salesforce MVP | Building Integrations that matter | MuleSoft Ambassador
1 年Wow awesome new toy! This will open up a lot of possibilities for integration using Flows! It definitely still requires some technical knowledge but this a great alternative compared to using Apex!
Salesforce Architect
1 年Great article and an interesting tool, thanks for sharing! I'd be lying if I didn't admit to the prevalence of point to point integration options now readily available heightening my anxiety. I see consultants having to unwind a lot more spaghetti architecture in the future!
Salesforce Solution Architect
1 年A great way to balance flow and code for functional programmers.
Architecture Lead at GravityLab, CTO for Five To Flow, 16x Salesforce Certified
1 年Something that we were talking about internally is just using this as a better json parser if you've already got a payload. This line will pull json out of a long text field and make it available for use in the same way as a normal request. Might be useful with omnistudio which has a json data structure (or so I'm told) $request = Json-Decode {!$Record}.Raw_Json__c Thanks to Matt and Neil from streamscript for guidance on this question.