API Error "Missed report parameter: [From]" when requesting report data.

LaineG
New Contributor II

The Situation

I am attempting to make an API request to get data from a report. This is the URL I use for the request:

https://api.servicetitan.io/reporting/v2/tenant/{my tenant ID}/report-category/operations/reports/1864/data?page=1&pageSize=100

"1864" corresponds with the Call Center Performance by CSR report.

The Code

I use the following Google Apps Script (modified JavaScript) code to make my request:

 

const csrReportId = 1864;
const appKey = /* secret  */;
// Try to over-request so that requests don't need to be repeated.
const requestItems = 100;
const requestUrl = `https://api.servicetitan.io/reporting/v2/tenant/${tenantId}/report-category/operations/reports/${csrReportId}/data?page=1&pageSize=${requestItems}`;
const csrRequestBase = {
  "method": "post",
  "headers": {
    "Content-Type": "application/x-www-form-urlencoded",
    "ST-App-Key": appKey
  },
  "payload": {
    "client_id": clientId,
    "client_secret": clientSecret
  }
};


function requestCsrReport()
{
  let csrRequest = addAuthToRequest(csrRequestBase);
  Logger.log(csrRequest["headers"]["ST-App-Key"]);
  let request = UrlFetchApp.fetch(requestUrl, csrRequest);
  Logger.log(request.getContentText());  
}

 

*addAuthToRequest is a custom function in a different file. That code is later in this post.

I have tested this code and verified it works with other parts of the API (I tested getting report categories using /report-categories and getting reports in a category using /{report_category}/reports).

The Error

The API returns a 400 error for the request, saying "Missed report parameter: [From]". Here is the full response:

{"type":"https://tools.ietf.org/html/rfc7231#section-6.5.1","title":"Missed report parameter: [From]","status":400,"traceId":"7e6335346b322d22-IAD","errors":{"":["Missed report parameter: [From]"]},"data":{"ErrorCode":"MissedReportParameter"}}

 Desired Result

I would like to edit my request so that the API successfully returns the report's data.

(Extra) Full Code and cURL equivalent

Here is the full content of the other file:

 

const tokenUrl = "https://auth.servicetitan.io/connect/token";
const clientId = /* secret! */;
const clientSecret = /* very secret!! >:O */;
const tenantId = /* a little secret? */;
const accessRequest = {
  "method": "post",
  "header": {
    "Content-Type": "application/x-www-form-urlencoded"
  },
  "payload": {
    "grant_type": "client_credentials",
    "client_id": clientId,
    "client_secret": clientSecret
  }
};

// Add the access token to the given request's headers and return modified request.
function addAuthToRequest(request)
{
  request["headers"]["Authorization"] = getAccessToken();
  return request;
}

// Gets the current OAuth 2.0 access token or requests a new one if it is expired.
function getAccessToken()
{
  let accessToken = requestAccessToken();
  // Commented out to make debugging easier
  // let accessToken = CacheService.getScriptCache().get("access-token");
  Logger.log(accessToken);
  if(accessToken !== null)
    return accessToken;
  else
    return requestAccessToken();
}

// Requests and returns an OAuth 2.0 access token from the ServiceTitan production environment.
function requestAccessToken()
{
  let response = UrlFetchApp.fetch(tokenUrl, accessRequest);
  if(response.getResponseCode() === 200)
  {
    let json = JSON.parse(response);
    let accessToken = json["access_token"];
    let expireSeconds = Date.now() + json["expires_in"];
    // Store the access token in the cache until it is expired.
    CacheService.getScriptCache().put("access-token", accessToken, expireSeconds);
    return accessToken;
  }
  else
    throw new Error(`ServiceTitan gave response code ${response.getResponseCode()}.`);
}

 

Here is the cURL equivalent of my request, which gives the same error:

curl --request POST --url "https://api.servicetitan.io/reporting/v2/tenant/{my tenant id here}/report-category/operations/reports/1864/data?page=1&pageSize=100" --header "ST-App-Key: {my app key}" --header "Authorization: {my valid OAuth 2.0 token}" --data client_id={my client id} --data client_secret={my client secret}

As a side note, I have also tried adding a "From" parameter in the URL, headers, payload/data, and in various other places in my request, including as an array (i.e. ["From"]: 1). This has given the same error.

1 ACCEPTED SOLUTION

LaineG
New Contributor II

I resolved the issue with the Integrations team! The request body must contain a parameters object that sets the date range for the report.

We edited my code to this:

const csrRequestBase = {
  "method": "post",
  "headers": {
    "Content-Type": "application/json",
    "ST-App-Key": appKey,
    "client_id": clientId,
    "client_secret": clientSecret
  },
  // JSON.stringify needed so that Apps Script doesn't convert the "parameters" array into its type name.
  "payload": JSON.stringify({
    "parameters": [
      {
        "name": "From",
        "value": "2023-07-01"
      },
      {
        "name": "To",
        "value": "2023-07-11"
      }
    ]
  })
};

 We also found that the Content-Type header must be "application/json".

View solution in original post

3 REPLIES 3

LaineG
New Contributor II

I resolved the issue with the Integrations team! The request body must contain a parameters object that sets the date range for the report.

We edited my code to this:

const csrRequestBase = {
  "method": "post",
  "headers": {
    "Content-Type": "application/json",
    "ST-App-Key": appKey,
    "client_id": clientId,
    "client_secret": clientSecret
  },
  // JSON.stringify needed so that Apps Script doesn't convert the "parameters" array into its type name.
  "payload": JSON.stringify({
    "parameters": [
      {
        "name": "From",
        "value": "2023-07-01"
      },
      {
        "name": "To",
        "value": "2023-07-11"
      }
    ]
  })
};

 We also found that the Content-Type header must be "application/json".

LBabayan
ServiceTitan Certified Provider
ServiceTitan Certified Provider

Hi @LaineG and thanks for bringing this to our attention! 

Are you still getting the error? Let me know if you need help with this so I resubmit a case to support for further investigation. Thank you for letting us assist you!

LaineG
New Contributor II

Yes, I am still having this error. I am working with the Integrations team to try to resolve the issue. Any additional support would be appreciated, as we have not yet found a solution.

Thanks!