Marketing Cloud API error: “Empty or Incorrect Authorization Token” thrown when correct access token is supplied

ampscriptjourneybuildermarketing-cloudrest-api

While this appears to be a straightforward, descriptive error message, I think there may be more to it…

The context: I am attempting to fire an entry event for a journey using the interaction/v1/events REST endpoint and am receiving a 401  Unauthorized response, which the documentation indicates is due to "Empty or Incorrect Authorization Token" even though the access token is supplied.

The full error message is as follows:

"An error occurred when attempting to evaluate a HTTPPost2 function call. See inner exception for details."
"ExactTarget.OMM.FunctionExecutionException: An error occurred when attempting to evaluate a HTTPPost2 function call. See inner exception for details.\r\n Error Code: OMM_FUNC_EXEC_ERROR\r\n - from Jint --> \r\n\r\n --- inner exception 1---\r\n\r\nSystem.Net.WebException: The remote server returned an error: (401) Unauthorized. - from System\r\n\r\n\r\n\r\n"

I have checked:

  • the auth call returns a token
  • the installed package has the permissions required by the documentation (list_and_subscribers_read),  to execute the second method

I've included the code below – having stripped out anything superfluous. Note I'm using a combination of AMPScript and SSJS  because elsewhere I'll be using methods only available in AMPScript, but AMPSCript doesn't have a built in method for parsing JSON or handling errors – my understanding is this is relatively common practice.

The code:

<script runat="server">
Platform.Load("Core","1.1.1");
try{
</script>
%%[
  set @url = "https://XXXXXXXXXXXX.auth.marketingcloudapis.com/v2/token"
  set @content = "application/json"
  set @payload = '{
    "grant_type":"client_credentials",
    "client_id":"XXXXXXXXXXXX",
    "client_secret":"XXXXXXXXXXXXX",
    "scope": "list_and_subscribers_read",
    "account_id": "1111111111"
    }';
  var @response1
  set @HTTP1 = HTTPPost2(@url, @content, @payload, true, @response1)
]%%
<script runat="server">
  Platform.Load("Core", "1.1.1");
  var str = Variable.GetValue("@response1");
  var obj = Platform.Function.ParseJSON(str);
  Variable.SetValue("@access_token",obj.access_token);
  Variable.SetValue("@token_type",obj.token_type);
  Variable.SetValue("@expires_in",obj.expires_in);
  Variable.SetValue("@scope",obj.scope);
  Variable.SetValue("@rest_instance_url",obj.rest_instance_url);
</script>
%%[
  var @response2
  set @content = "application/json"
  set @headerNames = "Authorization"
  set @headerValues = "Bearer " + @access_token
  set @url = "https://XXXXXXXXXXXX.rest.marketingcloudapis.com/interaction/v1/events"
  set @payload = '{
    "Contactkey":"XXXXXXXXXX",
    "EventDefinitionKey":"APIEvent-XXXXXXXXXXXXX",
    "Data": {
      "MobileNumber":"8888888888",
      "VerifyCode":"888888",
      "Locale":"en-nz"
    }
  }';
  /*THE NEXT LINE RETURNS THE ERROR*/
  set @HTTP2 = HTTPPost2(@url, @content, @payload, true, @response2)
]%%
<script runat="server">
}catch(e){
  Write(Stringify(e.message)+"
");
  Write(Stringify(e.description)+"
");
}
</script>

<!-- THEN, IN THE HTML BODY -->
Access Token: %%=V(@access_token)=%%<br/>
Token Type: %%=V(@token_type)=%%<br/>
Expires in: %%=V(@expires_in)=%%<br/>
Scope: %%=V(@scope)=%%<br/>
Instance URL: %%=V(@rest_instance_url)=%%<br/>

The output of the Auth response are all as expected:

Please advise whether there are any obvious errors in my approach – or should this be a support case? Any other observations warmly welcomed!

Best Answer

I'd definitely stick with SSJS for all of the REST API part -- you don't need to bounce around between them to troubleshoot it. I use a debug flag to conditional output the debugging statements.

<script runat="server" language="JavaScript">

  Platform.Load("Core","1");

  var debug = true;

  var url = 'https://xxxx.auth.marketingcloudapis.com/v2/token';
  var contentType = 'application/json';
  var mid = 111111;
  var payload = '{"grant_type":"client_credentials","client_id":"xxxx","client_secret":"xxxx","account_id":"'+mid+'"}';

  try {
  
    var result = HTTP.Post(url, contentType, payload);

    if (debug) {
      Write("<br>result (token): " + Stringify(result))
    }

    var statusCode = result["StatusCode"];
    var accessToken = Platform.Function.ParseJSON(result["Response"][0]).access_token;

  } catch (e) {

    if (debug) {
      Write("<br>e: " + Stringify(e))
    }
    
  }

  var url = "https://xxxx.rest.marketingcloudapis.com/interaction/v1/events";
  var contentType = 'application/json';
  var headerNames = ["Authorization"];
  var headerValues = ["Bearer " + accessToken];
  var payload = '{"Contactkey":"XXXXXXXXXX","EventDefinitionKey":"APIEvent-XXXXXXXXXXXXX","Data": {"MobileNumber":"8888888888","VerifyCode":"888888","Locale":"en-nz"}}';

  try {

    var result = HTTP.Post(url, contentType, payload, headerNames, headerValues);

    if (debug) {
      Write("<br>result (journey): " + Stringify(result))
    }

    // parse results and set AMPscript vars here

  } catch (e) {

    if (debug) {
      Write("<br>e: " + Stringify(e))
    }

  }

</script>

Related Topic