Skip to main content

Notifications

Community site session details

Community site session details

Session Id :
Power Automate - Using Flows
Answered

Content-Type header seems to be ignored with http action

(0) ShareShare
ReportReport
Posted on by

We're trying to call a REST API hosted in our NetSuite account. NetSuite requires a proprietary Authorization type. We tried providing the Authorization string in the Authentication field but Flow complained about the Type:

 

{ "Authorization":"NLAuth nlauth_account=xxxxxx, nlauth_email=xxxxxxx@xxxxx.com, nlauth_signature=xxxxxxxxxxxxxxx, nlauth_role=xxxx"}


We then tried defining the Authorization header in the main headers field and this worked, however we also need to define the content-type and this appears to be ignored.

 

{ "Authorization":"NLAuth nlauth_account=xxxxxxxxxx, nlauth_email=xxxxxxx@xxxxxxxx.com, nlauth_signature=xxxxxxxxx, nlauth_role=xxxx","Content-Type":"application/json" } 


The NetSuite REST api returns a JSON payload, if we don't explicitly set the Content-Type to "application/json" we get errors. If I temporarily trick the API to return only text, everything works fine which proves the Authorization header is working.

 

What are we doing wrong?

  • derekritchison Profile Picture
    2 on at
    Re: Content-Type header seems to be ignored with http action

    Hey all- Very old thread so apologies for resurrecting it, but this has been a useful starting point for my own PowerShell -> NetSuite API HRIS automations. That said, I am only getting the Invalid Login Attempt (401) error. I am using the above "Wall of Code" that was reworked for REST API, which my user/role/token is configured to use. Everything works in my Postman environment, but... not in my local PowerShell script.

    I would be forever grateful if we could dig back into this and see what might be wrong with my code or if something with the REST API has changed in the few years since the above code was working.

  • kevmaitland Profile Picture
    3 on at
    Re: Content-Type header seems to be ignored with http action

    Thanks to @rgmatthes1  - I wouldn't have persisted without your hints.

     

    Updated for REST API (still in beta, so YMMV).

     

    Wall-of-code:

     

    add-type @"
    using System.Net;
    using System.Security.Cryptography.X509Certificates;
    public class TrustAllCertsPolicy : ICertificatePolicy {
     public bool CheckValidationResult(
     ServicePoint srvPoint, X509Certificate certificate,
     WebRequest request, int certificateProblem) {
     return true;
     }
    }
    "@
    $AllProtocols = [System.Net.SecurityProtocolType]'Ssl3,Tls,Tls11,Tls12'
    [System.Net.ServicePointManager]::SecurityProtocol = $AllProtocols
    [System.Net.ServicePointManager]::CertificatePolicy = New-Object TrustAllCertsPolicy
    
    #--------REST v1.1 - Sharepoint Access, REST_Access_No2FA
    $oauth_consumer_key = "Integration Consumer Key here".ToUpper()
    $oauth_consumer_secret = "Integration Consumer Secret here".ToLower()
    $oauth_token = "Access Token ID here".ToUpper()
    $oauth_token_secret = "Access Token Secret here".ToLower()
    $oauth_signature_method = "HMAC-SHA256"
    $oauth_version = "1.0"
    $realm = "Your Realm" #This is *different* from the URL e.g. 1234567-sb1 becomes 1234567_SB1
    
    
    $HTTP_method = "GET"
    $url = "https://$($realm.ToLower().Replace("_","-")).suitetalk.api.netsuite.com"
    #$query = "/rest/platform/v1/metadata-catalog/record?select=customer"
    $query = "/rest/platform/v1/metadata-catalog/record/customer"
    if($query -match "\?"){
     $parameters = $query.Split("?")[1]
     $query = $query.Split("?")[0]
     }
    else{$parameters = ""}
    
    $oauth_nonce = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes([System.DateTime]::Now.Ticks.ToString()))
    $oauth_timestamp = [int64](([datetime]::UtcNow)-(Get-Date "1970-01-01")).TotalSeconds
    
    
    $oAuthParamsForSigning = @{}
    $oAuthParamsForSigning.Add("oauth_consumer_key",$oauth_consumer_key)
    $oAuthParamsForSigning.Add("oauth_token",$oauth_token)
    $oAuthParamsForSigning.Add("oauth_signature_method",$oauth_signature_method)
    $oAuthParamsForSigning.Add("oauth_version",$oauth_version)
    $oAuthParamsForSigning.Add("oauth_nonce",$oauth_nonce)
    $oAuthParamsForSigning.Add("oauth_timestamp",$oauth_timestamp)
    
    $parameters.Split("&") | % {
     if(![string]::IsNullOrWhiteSpace($_.Split("=")[0])){
     $oAuthParamsForSigning.Add($_.Split("=")[0],$_.Split("=")[1])
     }
     }
    $oAauthParamsString = ($oAuthParamsForSigning.Keys | Sort-Object | % {
     "$_=$($oAuthParamsForSigning[$_])"
     }) -join "&"
    $encodedOAuthParamsString = [uri]::EscapeDataString($oAauthParamsString)
    $encodedUrl = [uri]::EscapeDataString($url+$query)
    
    $base_string = $HTTP_method + "&" + $encodedUrl + "&" + $encodedOAuthParamsString
    $key = $oauth_consumer_secret + "&" + $oauth_token_secret
    $hmacsha265 = new-object System.Security.Cryptography.HMACSHA256
    $hmacsha265.Key = [System.Text.Encoding]::ASCII.GetBytes($key)
    $oauth_signature = [System.Convert]::ToBase64String($hmacsha265.ComputeHash([System.Text.Encoding]::ASCII.GetBytes($base_string)))
    #$oauth_signature - can be compared with PostMan and http://lti.tools/oauth/
    
    $authHeaderString = ($oAuthParamsForSigning.Keys | Sort-Object | % {
     "$_=`"$([uri]::EscapeDataString($oAuthParamsForSigning[$_]))`""
     }) -join ","
    $authHeaderString += ",realm=`"$([uri]::EscapeDataString($realm))`""
    $authHeaderString += ",oauth_signature=`"$([uri]::EscapeDataString($oauth_signature))`""
    
    
    $response = Invoke-RestMethod -Uri $([uri]::EscapeUriString($url+$query)) -Headers @{"Authorization"="OAuth $authHeaderString";"Cache-Control"="no-cache";"Accept"="application/swagger+json";"Accept-Encoding"="gzip, deflate"} -Method $HTTP_method -Verbose -ContentType "application/swagger+json"

     

     

    Broken into functions in a module:

     

    add-type @"
    using System.Net;
    using System.Security.Cryptography.X509Certificates;
    public class TrustAllCertsPolicy : ICertificatePolicy {
     public bool CheckValidationResult(
     ServicePoint srvPoint, X509Certificate certificate,
     WebRequest request, int certificateProblem) {
     return true;
     }
     }
    "@
    $AllProtocols = [System.Net.SecurityProtocolType]'Ssl3,Tls,Tls11,Tls12'
    [System.Net.ServicePointManager]::SecurityProtocol = $AllProtocols
    [System.Net.ServicePointManager]::CertificatePolicy = New-Object TrustAllCertsPolicy
    
    function get-netsuiteAuthHeaders(){
     [cmdletbinding()]
     Param (
     [parameter(Mandatory = $true)]
     [ValidateSet("GET","POST")]
     [string]$requestType
     
     ,[parameter(Mandatory = $true)]
     [ValidatePattern("http")]
     [string]$url
     
     ,[parameter(Mandatory=$true)]
     [hashtable]$oauthParameters
    
     ,[parameter(Mandatory=$true)]
     [string]$oauth_consumer_secret
    
     ,[parameter(Mandatory=$false)]
     [string]$oauth_token_secret
    
     ,[parameter(Mandatory=$true)]
     [string]$realm
     )
    
     $oauth_signature = get-oauthSignature -requestType $requestType -url $url -oauthParameters $oauthParameters -oauth_consumer_secret $oauth_consumer_secret -oauth_token_secret $oauth_token_secret
    
     $authHeaderString = ($oauthParameters.Keys | Sort-Object | % {
     "$_=`"$([uri]::EscapeDataString($oauthParameters[$_]))`""
     }) -join ","
     $authHeaderString += ",realm=`"$([uri]::EscapeDataString($realm))`""
     $authHeaderString += ",oauth_signature=`"$([uri]::EscapeDataString($oauth_signature))`""
     @{"Authorization"="OAuth $authHeaderString"
     ;"Cache-Control"="no-cache"
     ;"Accept"="application/swagger+json"
     ;"Accept-Encoding"="gzip, deflate"
     }
     }
    
    function get-netsuiteParameters(){
     [cmdletbinding()]
     Param()
     #Don't really store your keys and secrets in plaintext like this - it's just proof-of-concept
     @{oauth_consumer_key = "Integration Consumer Key here".ToUpper()
     ;oauth_consumer_secret = "Integration Consumer Secret here".ToLower()
     ;oauth_token = "Access Token ID here".ToUpper()
     ;oauth_token_secret = "Access Token Secret here".ToLower()
     ;oauth_signature_method = "HMAC-SHA256"
     ;oauth_version = "1.0"
     ;realm = "Your Realm"
     }
     }
    
    function get-oauthSignature(){
     [cmdletbinding()]
     Param (
     [parameter(Mandatory = $true)]
     [ValidateSet("GET","POST")]
     [string]$requestType
     
     ,[parameter(Mandatory = $true)]
     [ValidatePattern("http")]
     [string]$url
     
     ,[parameter(Mandatory=$true)]
     [hashtable]$oauthParameters
    
     ,[parameter(Mandatory=$true)]
     [string]$oauth_consumer_secret
    
     ,[parameter(Mandatory=$false)]
     [string]$oauth_token_secret
     )
     $requestType = $requestType.ToUpper()
     
     $encodedUrl = [uri]::EscapeDataString($url.ToLower())
    
     $oAauthParamsString = (
     $oauthParameters.Keys | Sort-Object | % {
     if(@("realm","oauth_signature") -notcontains $_){
     "$_=$($oauthParameters[$_])"
     }
     }
     ) -join "&"
     $encodedOAuthParamsString = [uri]::EscapeDataString($oAauthParamsString)
    
     $base_string = $requestType + "&" + $encodedUrl + "&" + $encodedOAuthParamsString
     $key = $oauth_consumer_secret + "&" + $oauth_token_secret
    
     Switch($oauthParameters["oauth_signature_method"]){
     "HMAC-SHA1" {
     $cryptoFunction = new-object System.Security.Cryptography.HMACSHA1
     }
     "HMAC-SHA256" {
     $cryptoFunction = new-object System.Security.Cryptography.HMACSHA256
     }
     "HMAC-SHA384" {
     $cryptoFunction = new-object System.Security.Cryptography.HMACSHA384
     }
     "HMAC-SHA512" {
     $cryptoFunction = new-object System.Security.Cryptography.HMACSHA512
     }
     default {
     Write-Error "Unsupported oauth_signature_method [$_]"
     break
     }
     }
    
     $cryptoFunction.Key = [System.Text.Encoding]::ASCII.GetBytes($key)
     $oauth_signature = [System.Convert]::ToBase64String($cryptoFunction.ComputeHash([System.Text.Encoding]::ASCII.GetBytes($base_string)))
     $oauth_signature
     }
    
    function invoke-netsuiteRestMethod(){
     [cmdletbinding()]
     Param(
     [parameter(Mandatory = $true)]
     [ValidateSet("GET","POST")]
     [string]$requestType
     
     ,[parameter(Mandatory = $true)]
     [ValidatePattern("http")]
     [string]$url
    
     ,[parameter(Mandatory=$false)]
     [hashtable]$netsuiteParameters
     )
    
     if(!$netsuiteParameters){$netsuiteParameters = get-netsuiteParameters}
     
     if($url -match "\?"){
     $parameters = $url.Split("?")[1]
     $url = $url.Split("?")[0]
     }
     else{$parameters = ""}
    
     $oauth_nonce = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes([System.DateTime]::Now.Ticks.ToString()))
     $oauth_timestamp = [int64](([datetime]::UtcNow)-(Get-Date "1970-01-01")).TotalSeconds
    
     $oAuthParamsForSigning = @{}
     #Add standard oAuth 1.0 parameters
     $oAuthParamsForSigning.Add("oauth_nonce",$oauth_nonce)
     $oAuthParamsForSigning.Add("oauth_timestamp",$oauth_timestamp)
     $oAuthParamsForSigning.Add("oauth_consumer_key",$netsuiteParameters.oauth_consumer_key)
     $oAuthParamsForSigning.Add("oauth_token",$netsuiteParameters.oauth_token)
     $oAuthParamsForSigning.Add("oauth_signature_method",$netsuiteParameters.oauth_signature_method)
     $oAuthParamsForSigning.Add("oauth_version",$netsuiteParameters.oauth_version)
     #Add parameters from url
     $parameters.Split("&") | % {
     if(![string]::IsNullOrWhiteSpace($_.Split("=")[0])){
     $oAuthParamsForSigning.Add($_.Split("=")[0],$_.Split("=")[1])
     }
     }
     
     $netsuiteRestHeaders = get-netsuiteAuthHeaders -requestType $requestType -url $url -oauthParameters $oAuthParamsForSigning -oauth_consumer_secret $netsuiteParameters["oauth_consumer_secret"] -oauth_token_secret $netsuiteParameters["oauth_token_secret"] -realm $netsuiteParameters["realm"]
     
     $response = Invoke-RestMethod -Uri $([uri]::EscapeUriString($url)) -Headers $netsuiteRestHeaders -Method $requestType -Verbose -ContentType "application/swagger+json"
     $response 
     }

     

     

    Usage:

     

    invoke-netsuiteRestMethod -requestType GET -url "https://YourInstance.suitetalk.api.netsuite.com/rest/platform/v1/metadata-catalog/record/customer"

     

     

  • Verified answer
    rgmatthes1 Profile Picture
    11 on at
    Re: Content-Type header seems to be ignored with http action

    Old thread, but we had the same problem as you. Unfortunately, the solution isn't pretty. We ended up doing two things:

     

    • Using OAuth instead of NLAuth. Passing account information such as a username and password isn't just insecure, it's fragile. If this user leaves the company, or your service account is decommissioned, stuff starts to break, and it's usually hard to pinpoint where. So we wrote a function app to generate an OAuth header for making calls into NetSuite. You can call the function app from within Logic Apps and have it return the header you need to make your HTTP request to NetSuite. Here's a starting point in PowerShell, but there are other Python examples out there if you Google: https://netsuitehub.com/forums/topic/oauth-powershell-restlet-working-script-example

      This script didn't take us all the way there. I don't think I'm allowed to post my final code, but:
      • You need to sort alphabetically all parameters and encode all values when constructing the signature (including "deploy" and "script"), which this code doesn't do. Powershell "sort" is your friend.
      • This script's nonce generation stinks. You don't want to limit yourself to just a random number, but a random, case-sensitive string. That way you have 62 characters at your disposal instead of 10.
      • Our base URL frustratingly did not start with "rest," but our NS account ID #. None of the NetSuite documentation included this, but we noticed when deploying our script that its external URL was formed differently.
      • Here's a site you can use to check your script-generated signature vs. what NetSuite is looking for: http://lti.tools/oauth/
    • Write our own HTTP request. Again, function apps to the rescue. Instead of using using the built-in Logic Apps step, which as you noted doesn't work well with NetSuite, we made another function app that uses the "Invoke-WebRequest" PowerShell cmdlet to make the web request, then return the result back to Logic Apps. This was just 10 lines of code including comments, so it was far easier than the last bit to write. My only word of advice is to remember that you can submit multiple headers via an HTTP request, which means you need to expand the JSON and go through each one (think "PSObject.Properties") and add the names and values to a hashtable, which you can reference when you get to the "Invoke-WebRequest" step.

    The two steps worked together for us. First we feed the URL and HTTP method (GET, POST, etc.) to the OAuth header generator, then we use the header to form a full HTTP request and via the second function app. This should be scaleable as you tackle more and more autoation with Azure.

  • Community Power Platform Member Profile Picture
    on at
    Re: Content-Type header seems to be ignored with http action

    You're trying exactly the same integration I am planning to do. Keep this thread update, please!

  • v-yamao-msft Profile Picture
    on at
    Re: Content-Type header seems to be ignored with http action

    Hi Sklett,

     

    This article about Custom APIs can be a reference for you:
    https://powerapps.microsoft.com/en-us/tutorials/register-custom-api/

     

    There is a note in the document stating “Support for API key authentication is coming soon”.

     

    The article also has links for AAD Authentication that may be helpful.

     

    Best regards,
    Mabel Mao

Under review

Thank you for your reply! To ensure a great experience for everyone, your content is awaiting approval by our Community Managers. Please check back later.

Helpful resources

Quick Links

🌸 Community Spring Festival 2025 Challenge 🌸

WIN Power Platform Community Conference 2025 tickets!

Markus Franz – Community Spotlight

We are honored to recognize Markus Franz as our April 2025 Community…

Kudos to the March Top 10 Community Stars!

Thanks for all your good work in the Community!

Leaderboard

#1
WarrenBelz Profile Picture

WarrenBelz 146,631 Most Valuable Professional

#2
RandyHayes Profile Picture

RandyHayes 76,287 Super User 2024 Season 1

#3
Pstork1 Profile Picture

Pstork1 65,991 Most Valuable Professional

Leaderboard

Featured topics

Restore a deleted flow